Files
truf-server/tests/test_result_bundle_v2.py
T
2026-09-30 20:30:56 +03:00

709 lines
32 KiB
Python

import os
import json
import base64
import hashlib
import io
from pathlib import Path
import sys
import tempfile
import unittest
from datetime import datetime, timezone
from unittest import mock
from types import SimpleNamespace
ROOT = Path(__file__).resolve().parents[1]
APP_DIR = ROOT / 'app'
sys.path.insert(0, str(APP_DIR))
from result_bundle import (
BundleReservation,
ResultBundleConflictError,
ResultBundleError,
ResultBundleReader,
ResultBundleWriter,
)
from runtime_security import ensure_private_directory, harden_private_file
import runtime_security
import result_bundle
import console_runner
import scanner
import worker_contracts
from worker_contracts import (
AssignmentOutcome,
DiagnosticCategory,
DiagnosticKind,
ScanOutcome,
WorkerPhase,
build_diagnostic_envelope,
decode_diagnostic_envelope,
diagnostic_material_bytes,
encode_diagnostic_envelope,
)
from result_ingester import ResultIngester
def reservation():
bundle_id = 'a' * 32
return BundleReservation(
reservation_id=7,
reservation_token='reservation-token',
bundle_id=bundle_id,
scan_event_id='b' * 32,
queue_id=11,
claim_lease_token='lease-token',
declared_bytes=1024 * 1024,
ready_path=f'ready/{bundle_id[:2]}/{bundle_id}.trb',
source='github',
platform='github',
target='https://example.invalid/repo',
normalized_target='https://example.invalid/repo',
)
def diagnostic(
*, occurrence_id='bundle-diagnostic', reservation_id=7,
assignment_outcome=AssignmentOutcome.ACCEPTED,
scan_outcome=ScanOutcome.ERROR,
):
timestamp = '2026-09-24T00:00:00Z'
return build_diagnostic_envelope(
occurrence_id=occurrence_id,
reservation_id=reservation_id,
scan_event_id='b' * 32,
slot_id=0,
source='github',
phase=WorkerPhase.SCANNING,
kind=DiagnosticKind.ASSIGNMENT,
category=DiagnosticCategory.SCANNER,
code='scan.fixture_error',
summary='fixture diagnostic',
retryable=False,
attempt=1,
assignment_outcome=assignment_outcome,
scan_outcome=scan_outcome,
occurred_at=timestamp,
captured_at=timestamp,
)
class ResultBundleV2Tests(unittest.TestCase):
def private_root(self, temp_dir):
root = os.path.join(temp_dir, 'bundles')
ensure_private_directory(root, reject_reparse=True)
return root
def test_round_trip_streams_frames_and_exact_footer(self):
with tempfile.TemporaryDirectory() as temp_dir:
root = self.private_root(temp_dir)
with ResultBundleWriter.open(root, reservation()) as writer:
writer.write_finding({'DetectorName': 'OpenAI', 'Raw': 'secret'})
writer.write_error('fixture error')
writer.write_candidate({'service': 'openai', 'secret_text': 'secret'})
commit = writer.finish({'status': 'found', 'findings_count': 1})
reader = ResultBundleReader(os.path.join(root, *commit.relative_path.split('/')))
metadata = reader.validate()
self.assertEqual(metadata.scan_event_hash, commit.scan_event_hash)
self.assertEqual(metadata.actual_bytes, commit.actual_bytes)
self.assertEqual(list(reader.iter_findings())[0]['DetectorName'], 'OpenAI')
self.assertEqual(list(reader.iter_errors()), ['fixture error'])
self.assertEqual(list(reader.iter_candidates())[0]['service'], 'openai')
self.assertEqual(reader.metadata()['status'], 'found')
def test_diagnostic_frames_are_canonical_counted_and_identity_checked(self):
with tempfile.TemporaryDirectory() as temp_dir:
root = self.private_root(temp_dir)
expected = diagnostic()
with ResultBundleWriter.open(root, reservation()) as writer:
writer.write_error('fixture error remains an E frame')
writer.write_diagnostic(expected)
commit = writer.finish({'status': 'error'})
reader = ResultBundleReader(os.path.join(root, *commit.relative_path.split('/')))
metadata = reader.validate()
values = list(reader.iter_diagnostics())
self.assertEqual(metadata.diagnostic_count, 1)
self.assertEqual(
metadata.diagnostic_bytes,
result_bundle.FRAME_HEADER.size + len(encode_diagnostic_envelope(expected)),
)
self.assertEqual(
values,
[json.loads(encode_diagnostic_envelope(expected).decode('ascii'))],
)
self.assertEqual(list(reader.iter_errors()), ['fixture error remains an E frame'])
with self.assertRaisesRegex(ResultBundleError, 'identity conflicts'):
with ResultBundleWriter.open(root, BundleReservation(
**{**reservation().__dict__, 'bundle_id': 'c' * 32,
'ready_path': 'ready/cc/' + ('c' * 32) + '.trb'}
)) as writer:
writer.write_diagnostic(diagnostic(reservation_id=8))
def test_diagnostic_duplicate_aggregate_order_and_legacy_footer_are_strict(self):
with tempfile.TemporaryDirectory() as temp_dir:
root = self.private_root(temp_dir)
with ResultBundleWriter.open(root, reservation()) as writer:
commit = writer.finish({'status': 'clean'})
legacy_reader = ResultBundleReader(
os.path.join(root, *commit.relative_path.split('/'))
)
legacy_reader.validate()
footer = list(legacy_reader._iter_frames())[-1][1]
self.assertNotIn('diagnostic_count', footer)
self.assertNotIn('diagnostic_bytes', footer)
with tempfile.TemporaryDirectory() as temp_dir:
root = self.private_root(temp_dir)
writer = ResultBundleWriter.open(root, reservation())
writer.write_diagnostic(diagnostic())
with self.assertRaisesRegex(ResultBundleError, 'duplicated'):
writer.write_diagnostic(diagnostic())
writer.abort()
def test_dless_error_without_timestamps_uses_fixed_replay_timestamp(self):
projected = []
for _index in range(2):
with tempfile.TemporaryDirectory() as temp_dir:
root = self.private_root(temp_dir)
with ResultBundleWriter.open(root, reservation()) as writer:
writer.write_error('legacy timestamp-free error')
commit = writer.finish({'status': 'error'})
reader = ResultBundleReader(
os.path.join(root, *commit.relative_path.split('/'))
)
projected.append(reader.effective_diagnostics())
self.assertEqual(projected[0], projected[1])
self.assertEqual(
projected[0][0]['occurred_at'],
worker_contracts.LEGACY_DIAGNOSTIC_FALLBACK_TIMESTAMP,
)
for outcome, scan_outcome, metadata_status in (
(AssignmentOutcome.PREBUNDLE_FAILED, ScanOutcome.UNAVAILABLE, 'error'),
(AssignmentOutcome.ACCEPTED, ScanOutcome.CLEAN, 'error'),
):
with self.subTest(
outcome=outcome, scan_outcome=scan_outcome,
), tempfile.TemporaryDirectory() as temp_dir:
root = self.private_root(temp_dir)
with ResultBundleWriter.open(root, reservation()) as writer:
writer.write_diagnostic(diagnostic(
assignment_outcome=outcome,
scan_outcome=scan_outcome,
))
commit = writer.finish({'status': metadata_status})
with self.assertRaisesRegex(ResultBundleError, 'outcome'):
ResultBundleReader(
os.path.join(root, *commit.relative_path.split('/'))
).validate()
with tempfile.TemporaryDirectory() as temp_dir:
root = self.private_root(temp_dir)
writer = ResultBundleWriter.open(root, reservation())
invalid = json.loads(encode_diagnostic_envelope(diagnostic()))
invalid['source'] = ' '
with self.assertRaisesRegex(ResultBundleError, 'diagnostic frame'):
writer.write_diagnostic(invalid)
writer.abort()
with tempfile.TemporaryDirectory() as temp_dir:
root = self.private_root(temp_dir)
writer = ResultBundleWriter.open(root, reservation())
writer.write_candidate({'service': 'openai'})
with self.assertRaisesRegex(ResultBundleError, 'order'):
writer.write_diagnostic(diagnostic())
writer.abort()
with tempfile.TemporaryDirectory() as temp_dir:
root = self.private_root(temp_dir)
writer = ResultBundleWriter.open(root, reservation())
with self.assertRaisesRegex(ResultBundleError, 'aggregate'):
for index in range(32):
value = build_diagnostic_envelope(
occurrence_id=f'aggregate-{index}', reservation_id=7,
scan_event_id='b' * 32, slot_id=0, source='github',
phase=WorkerPhase.SCANNING, kind=DiagnosticKind.ASSIGNMENT,
category=DiagnosticCategory.SCANNER, code='scan.aggregate',
summary='x' * 9000, retryable=False, attempt=1,
assignment_outcome=AssignmentOutcome.ACCEPTED,
scan_outcome=ScanOutcome.ERROR,
occurred_at='2026-09-24T00:00:00Z',
captured_at='2026-09-24T00:00:00Z',
)
writer.write_diagnostic(value)
writer.abort()
def test_scan_diagnostic_preserves_preparse_raw_process_material(self):
secret = 'raw-provider-token-evidence'
raw_stderr = json.dumps({
'level': 'error', 'msg': f'fatal {secret}',
}) + '\n'
result = {'findings': [], 'errors': []}
with scanner.streamed_output_from_text('', raw_stderr, 1) as output:
scanner.apply_trufflehog_diagnostics(
result, output, output.returncode, 'git',
redactions=(secret,),
)
result.update({
'target': reservation().target,
'scan_type': reservation().platform,
'scan_event_id': reservation().scan_event_id,
'scan_started_at': '2026-09-24T00:00:00Z',
'timestamp': '2026-09-24T00:00:01Z',
'duration_sec': 1.0,
})
with tempfile.TemporaryDirectory() as temp_dir:
root = self.private_root(temp_dir)
staged = scanner.stage_result_bundle(
result, reservation(), root, {}, {'queue_status': 'failed'},
)
reader = ResultBundleReader(
os.path.join(root, *staged.relative_path.split('/'))
)
diagnostic_value = list(reader.iter_diagnostics())[0]
envelope = decode_diagnostic_envelope(json.dumps(
diagnostic_value, ensure_ascii=True, sort_keys=True,
separators=(',', ':'),
).encode('ascii'))
self.assertEqual(
diagnostic_material_bytes(envelope.process.stderr),
raw_stderr.encode('utf-8'),
)
self.assertIn(secret, diagnostic_material_bytes(
envelope.process.stderr,
).decode('utf-8'))
self.assertNotIn(secret, '\n'.join(reader.iter_errors()))
self.assertIn('pre-existing configured parser redactions',
envelope.exception.message)
def test_scan_diagnostic_preserves_raw_http_response_body(self):
import requests
response = requests.Response()
response.status_code = 503
response._content = b'\xffraw-provider-body\x00'
response.headers['Content-Type'] = 'application/octet-stream'
failure = requests.HTTPError('provider request failed', response=response)
with mock.patch.object(scanner, 'scan_git_repo', side_effect=failure):
result = scanner.scan_target_result(
reservation().target, 'github', reservation().scan_event_id, {},
)
with tempfile.TemporaryDirectory() as temp_dir:
root = self.private_root(temp_dir)
staged = scanner.stage_result_bundle(
result, reservation(), root, {}, {'queue_status': 'failed'},
)
reader = ResultBundleReader(
os.path.join(root, *staged.relative_path.split('/'))
)
envelope = decode_diagnostic_envelope(json.dumps(
list(reader.iter_diagnostics())[0], ensure_ascii=True,
sort_keys=True, separators=(',', ':'),
).encode('ascii'))
self.assertEqual(envelope.http.status_code, 503)
self.assertEqual(
diagnostic_material_bytes(envelope.http.body), response.content,
)
def test_naive_legacy_scan_timestamp_is_not_relabelled_as_utc(self):
result = {
'target': reservation().target,
'scan_type': reservation().platform,
'scan_event_id': reservation().scan_event_id,
'scan_started_at': '2020-01-01T12:00:00',
'timestamp': '2020-01-01T12:00:01',
'duration_sec': 1.0,
'findings': [],
'errors': ['legacy scanner failure'],
}
before = datetime.now(timezone.utc)
with tempfile.TemporaryDirectory() as temp_dir:
root = self.private_root(temp_dir)
staged = scanner.stage_result_bundle(
result, reservation(), root, {}, {'queue_status': 'failed'},
)
diagnostic_value = list(ResultBundleReader(os.path.join(
root, *staged.relative_path.split('/'),
)).iter_diagnostics())[0]
after = datetime.now(timezone.utc)
envelope = decode_diagnostic_envelope(json.dumps(
diagnostic_value, ensure_ascii=True, sort_keys=True,
separators=(',', ':'),
).encode('ascii'))
occurred = datetime.fromisoformat(
envelope.occurred_at.replace('Z', '+00:00'),
)
self.assertGreaterEqual(occurred, before)
self.assertLessEqual(occurred, after)
def test_build_api_error_and_git_adapter_preserve_raw_http_evidence(self):
import requests
response = requests.Response()
response.status_code = 429
response._content = b'{"message":"raw rate body"}\x00'
response._content_consumed = True
response.headers.update({
'Content-Type': 'application/json',
'X-GitHub-Request-Id': 'request-fixture',
'Retry-After': '60',
})
error = scanner.github_api_error(response)
self.assertEqual(error.diagnostic_http['status_code'], 429)
self.assertEqual(
base64.b64decode(error.diagnostic_http['body_b64']),
response.content,
)
claim = {
**reservation().__dict__,
'reservation_id': reservation().reservation_id,
'scan_event_id': reservation().scan_event_id,
'target': reservation().target,
}
failure = console_runner._GitResolutionFailure(error)
result = console_runner.git_resolution_failure_result(
SimpleNamespace(platform='github'), claim, {'token': ''}, failure,
)
self.assertEqual(result['_diagnostic_http'], error.diagnostic_http)
with tempfile.TemporaryDirectory() as temp_dir:
root = self.private_root(temp_dir)
staged = scanner.stage_result_bundle(
result, reservation(), root, {}, {'queue_status': 'deferred'},
)
reader = ResultBundleReader(
os.path.join(root, *staged.relative_path.split('/'))
)
envelope = decode_diagnostic_envelope(json.dumps(
list(reader.iter_diagnostics())[0], ensure_ascii=True,
sort_keys=True, separators=(',', ':'),
).encode('ascii'))
self.assertEqual(envelope.http.status_code, 429)
self.assertEqual(envelope.http.request_id, 'request-fixture')
self.assertEqual(
diagnostic_material_bytes(envelope.http.body), response.content,
)
captured_headers = json.loads(
diagnostic_material_bytes(envelope.http.headers).decode('ascii')
)
self.assertEqual(
captured_headers['X-GitHub-Request-Id'], 'request-fixture',
)
self.assertIn('parsed response headers', envelope.exception.message)
def test_streamed_api_error_body_is_captured_once_with_explicit_bound(self):
import requests
for payload, truncated in (
(b'{"message":"small streamed body"}', False),
(b'x' * (worker_contracts.MAX_DIAGNOSTIC_BODY_BYTES + 4096), True),
):
with self.subTest(truncated=truncated):
response = requests.Response()
response.status_code = 429
response.raw = io.BytesIO(payload)
response._content = False
response._content_consumed = False
response.headers['Content-Type'] = 'application/octet-stream'
message = scanner.response_message(response)
error = scanner.build_api_error(
'github', 'rate_limit', message, response,
)
captured = base64.b64decode(
error.diagnostic_http['body_b64']
)
self.assertEqual(
captured,
payload[:worker_contracts.MAX_DIAGNOSTIC_BODY_BYTES],
)
self.assertEqual(
error.diagnostic_http['body_capture_truncated'], truncated,
)
self.assertEqual(
error.diagnostic_http['body_original_size'], len(payload),
)
self.assertEqual(
error.diagnostic_http['body_stored_size'], len(captured),
)
self.assertEqual(
error.diagnostic_http['body_sha256'],
hashlib.sha256(payload).hexdigest(),
)
claim = {
**reservation().__dict__,
'reservation_id': reservation().reservation_id,
'scan_event_id': reservation().scan_event_id,
'target': reservation().target,
}
result = console_runner.git_resolution_failure_result(
SimpleNamespace(platform='github'), claim, {'token': ''},
console_runner._GitResolutionFailure(error),
)
with tempfile.TemporaryDirectory() as temp_dir:
root = self.private_root(temp_dir)
staged = scanner.stage_result_bundle(
result, reservation(), root, {}, {'queue_status': 'deferred'},
)
reader = ResultBundleReader(
os.path.join(root, *staged.relative_path.split('/'))
)
envelope = decode_diagnostic_envelope(json.dumps(
list(reader.iter_diagnostics())[0], ensure_ascii=True,
sort_keys=True, separators=(',', ':'),
).encode('ascii'))
self.assertEqual(
diagnostic_material_bytes(envelope.http.body), captured,
)
self.assertEqual(envelope.http.body.original_size, len(payload))
self.assertEqual(envelope.http.body.stored_size, len(captured))
self.assertEqual(
envelope.http.body.sha256, hashlib.sha256(payload).hexdigest(),
)
self.assertIs(envelope.http.body.truncated, truncated)
def test_staging_rejects_cross_reservation_result_identity_before_publish(self):
with tempfile.TemporaryDirectory() as temp_dir:
root = self.private_root(temp_dir)
base = {
'reservation_id': 7,
'queue_id': 11,
'scan_event_id': 'b' * 32,
'source': 'github',
'normalized_target': 'https://example.invalid/repo',
'scan_type': 'github',
'target': 'https://example.invalid/repo',
}
cases = {
'reservation': {'reservation_id': 8},
'queue': {'queue_id': 12},
'event': {'scan_event_id': 'c' * 32},
'source': {'source': 'gitlab'},
'normalized_target': {
'normalized_target': 'https://secret.invalid/cross-reservation',
},
'target': {'target': 'https://secret.invalid/cross-reservation'},
}
for name, mismatch in cases.items():
with self.subTest(name=name):
result = {
**base, **mismatch,
'findings': [{'Raw': 'SECRET_SENTINEL'}],
'errors': [],
}
with self.assertRaisesRegex(ValueError, 'identity') as raised:
scanner.stage_result_bundle(
result, reservation(), root, {},
{'queue_status': 'done'},
)
self.assertNotIn('secret.invalid', str(raised.exception))
self.assertNotIn('SECRET_SENTINEL', str(raised.exception))
self.assertFalse(os.path.exists(os.path.join(root, 'ready')))
def test_truncation_and_noncanonical_payload_are_rejected(self):
with tempfile.TemporaryDirectory() as temp_dir:
root = self.private_root(temp_dir)
with ResultBundleWriter.open(root, reservation()) as writer:
commit = writer.finish({'status': 'clean'})
path = os.path.join(root, *commit.relative_path.split('/'))
payload = Path(path).read_bytes()
Path(path).write_bytes(payload[:-1])
harden_private_file(path)
with self.assertRaises(ResultBundleError):
ResultBundleReader(path).validate()
def test_reader_rejects_ready_file_replacement_after_validation(self):
with tempfile.TemporaryDirectory() as first_dir, tempfile.TemporaryDirectory() as second_dir:
first_root = self.private_root(first_dir)
second_root = self.private_root(second_dir)
with ResultBundleWriter.open(first_root, reservation()) as writer:
writer.write_finding({'DetectorName': 'OpenAI'})
first = writer.finish({'status': 'found'})
with ResultBundleWriter.open(second_root, reservation()) as writer:
writer.write_finding({'DetectorName': 'GitLab'})
second = writer.finish({'status': 'found'})
first_path = os.path.join(first_root, *first.relative_path.split('/'))
second_path = os.path.join(second_root, *second.relative_path.split('/'))
reader = ResultBundleReader(first_path)
reader.validate()
os.replace(second_path, first_path)
with self.assertRaisesRegex(ResultBundleError, 'changed after validation'):
list(reader.iter_findings())
def test_reader_rejects_non_object_typed_frames(self):
with tempfile.TemporaryDirectory() as temp_dir:
root = self.private_root(temp_dir)
with ResultBundleWriter.open(root, reservation()) as writer:
writer.write_finding('not-an-object')
commit = writer.finish({'status': 'found'})
path = os.path.join(root, *commit.relative_path.split('/'))
with self.assertRaisesRegex(ResultBundleError, 'JSON object'):
ResultBundleReader(path).validate()
def test_temporarily_unavailable_private_file_is_not_a_validation_failure(self):
with tempfile.TemporaryDirectory() as temp_dir:
root = self.private_root(temp_dir)
with ResultBundleWriter.open(root, reservation()) as writer:
commit = writer.finish({'status': 'clean'})
path = os.path.join(root, *commit.relative_path.split('/'))
with mock.patch.object(result_bundle, 'private_file_ready', return_value=False):
with self.assertRaises(result_bundle.ResultBundleUnavailableError):
ResultBundleReader(path).validate()
def test_post_rename_fault_never_allows_abort_to_remove_ready_bundle(self):
def fault(stage, _writer):
if stage == 'after_rename':
raise RuntimeError('injected post-rename crash')
with tempfile.TemporaryDirectory() as temp_dir:
root = self.private_root(temp_dir)
writer = ResultBundleWriter.open(root, reservation(), fault=fault)
with self.assertRaisesRegex(RuntimeError, 'post-rename'):
writer.finish({'status': 'clean'})
writer.abort()
ready = os.path.join(root, *reservation().ready_path.replace('/', os.sep).split(os.sep))
self.assertTrue(os.path.isfile(ready))
self.assertEqual(ResultBundleReader(ready).validate().scan_event_id, 'b' * 32)
def test_post_publish_consumption_is_not_reported_as_acl_failure(self):
def consume(stage, writer):
if stage == 'after_rename':
os.remove(writer.ready_path)
with tempfile.TemporaryDirectory() as temp_dir:
root = self.private_root(temp_dir)
writer = ResultBundleWriter.open(root, reservation(), fault=consume)
commit = writer.finish({'status': 'clean'})
self.assertTrue(writer.finished)
self.assertEqual(commit.reservation_id, reservation().reservation_id)
self.assertFalse(os.path.exists(writer.ready_path))
def test_pre_handoff_fault_windows_never_publish_ready_name(self):
for stage in ('before_footer', 'before_fsync', 'before_rename'):
with self.subTest(stage=stage), tempfile.TemporaryDirectory() as temp_dir:
root = self.private_root(temp_dir)
def fault(current, _writer):
if current == stage:
raise OSError(f'injected {stage}')
writer = ResultBundleWriter.open(root, reservation(), fault=fault)
with self.assertRaisesRegex(OSError, stage):
writer.finish({'status': 'clean'})
writer.abort()
ready = os.path.join(
root, *reservation().ready_path.replace('/', os.sep).split(os.sep),
)
self.assertFalse(os.path.exists(ready))
def test_existing_deterministic_ready_name_is_never_replaced(self):
with tempfile.TemporaryDirectory() as temp_dir:
root = self.private_root(temp_dir)
with ResultBundleWriter.open(root, reservation()) as writer:
writer.finish({'status': 'clean'})
with self.assertRaises(ResultBundleConflictError):
ResultBundleWriter.open(root, reservation())
def test_frame_bound_fails_before_ready_publication(self):
with tempfile.TemporaryDirectory() as temp_dir:
root = self.private_root(temp_dir)
writer = ResultBundleWriter.open(root, reservation())
with self.assertRaises(ResultBundleError):
writer.write_finding({'value': 'x' * (1024 * 1024)})
writer.abort()
ready = os.path.join(root, *reservation().ready_path.replace('/', os.sep).split(os.sep))
self.assertFalse(os.path.exists(ready))
def test_private_path_inspection_never_maps_storage_failure_to_absence(self):
with tempfile.TemporaryDirectory() as temp_dir:
root = self.private_root(temp_dir)
ensure_private_directory(os.path.join(root, 'ready', 'aa'), reject_reparse=True)
absent = runtime_security.inspect_private_relative_path(
root, 'ready/aa/' + ('a' * 32) + '.trb',
)
self.assertEqual(absent.state, runtime_security.PrivatePathState.ABSENT)
with mock.patch.object(runtime_security.os, 'scandir', side_effect=OSError('storage offline')):
unknown = runtime_security.inspect_private_relative_path(
root, 'ready/aa/' + ('a' * 32) + '.trb',
)
self.assertEqual(unknown.state, runtime_security.PrivatePathState.UNKNOWN)
self.assertIn('storage offline', unknown.detail)
def test_refund_retains_claim_when_partial_unlink_is_unconfirmed(self):
with tempfile.TemporaryDirectory() as temp_dir:
root = self.private_root(temp_dir)
value = reservation()
from result_bundle import bundle_partial_path, ensure_bundle_reservation_paths
ensure_bundle_reservation_paths(root, value)
partial = bundle_partial_path(root, value.bundle_id, value.reservation_token)
Path(partial).write_bytes(b'partial')
harden_private_file(partial)
class DB:
called = False
def refund_uncommitted_reservation(self, *args, **kwargs):
self.called = True
return True
db = DB()
claim = {
'reservation_id': value.reservation_id,
'reservation_token': value.reservation_token,
'bundle_id': value.bundle_id,
'ready_relative_path': value.ready_path,
}
with mock.patch.object(
console_runner, 'durable_unlink', side_effect=OSError('unlink unavailable'),
):
refunded = console_runner.refund_v2_claim_after_no_handoff(
db, root, claim, {}, 'fixture',
)
self.assertFalse(refunded)
self.assertFalse(db.called)
self.assertTrue(os.path.isfile(partial))
def test_terminal_reconciliation_revisits_unknown_artifact_state(self):
with tempfile.TemporaryDirectory() as temp_dir:
root = self.private_root(temp_dir)
class DB:
deleted = []
deferred = []
@staticmethod
def bundle_terminal_temp_artifacts(_limit):
return [{'id': 9, 'relative_path': 'tmp/aa/fixture.partial'}]
def mark_pipeline_artifact_deleted(self, artifact_id):
self.deleted.append(artifact_id)
return True
def defer_pipeline_artifact_cleanup(self, artifact_id, error):
self.deferred.append((artifact_id, error))
return True
db = DB()
worker = ResultIngester(db, root, 'fixture')
unknown = runtime_security.PrivatePathInspection(
runtime_security.PrivatePathState.UNKNOWN, 'fixture', 'storage unavailable',
)
absent = runtime_security.PrivatePathInspection(
runtime_security.PrivatePathState.ABSENT, 'fixture',
)
with mock.patch.object(worker, '_inspect', side_effect=[unknown, absent]):
worker.reconcile_terminal_artifacts(max_pages=1)
self.assertEqual(db.deleted, [])
self.assertEqual(db.deferred[0][0], 9)
worker.reconcile_terminal_artifacts(max_pages=1)
self.assertEqual(db.deleted, [9])
if __name__ == '__main__':
unittest.main()