feat(application): integrate measured-load ingestion training and planner source
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
import ast
|
||||
import hashlib
|
||||
import importlib.util
|
||||
import json
|
||||
from pathlib import Path
|
||||
from tempfile import TemporaryDirectory
|
||||
import unittest
|
||||
|
||||
from forecast_acceptance import source_paths, verify_forecast_bundle
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
spec = importlib.util.spec_from_file_location('forecast_release_test', ROOT / 'release_preflight.py')
|
||||
preflight = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(preflight)
|
||||
|
||||
|
||||
class ForecastAcceptanceTest(unittest.TestCase):
|
||||
def fixture(self, root):
|
||||
src, bundle = root / 'source', root / 'bundle'
|
||||
for path in (src, bundle):
|
||||
path.mkdir()
|
||||
for name, value in {'main.py': 'x=1\n', 'telemetry_quality.py': 'x=2\n', 'requirements.txt': 'pandas\n'}.items():
|
||||
(path / name).write_text(value)
|
||||
manifest = {p.name: hashlib.sha256(p.read_bytes()).hexdigest() for p in source_paths(src)}
|
||||
(bundle / 'SOURCE_MANIFEST.json').write_text(json.dumps(manifest))
|
||||
return src, bundle
|
||||
|
||||
def test_exact_source_is_verified(self):
|
||||
with TemporaryDirectory() as d:
|
||||
src, bundle = self.fixture(Path(d))
|
||||
self.assertEqual(len(verify_forecast_bundle(bundle, src)), 3)
|
||||
|
||||
def test_modified_development_source_blocks_test(self):
|
||||
with TemporaryDirectory() as d:
|
||||
src, bundle = self.fixture(Path(d))
|
||||
(src / 'main.py').write_text('x=3\n')
|
||||
with self.assertRaises(ValueError):
|
||||
verify_forecast_bundle(bundle, src)
|
||||
|
||||
def test_extra_staged_python_file_blocks_test(self):
|
||||
with TemporaryDirectory() as d:
|
||||
src, bundle = self.fixture(Path(d))
|
||||
(bundle / 'surprise.py').write_text('x=3\n')
|
||||
with self.assertRaises(ValueError):
|
||||
verify_forecast_bundle(bundle, src)
|
||||
|
||||
def test_symlink_is_rejected(self):
|
||||
with TemporaryDirectory() as d:
|
||||
src, bundle = self.fixture(Path(d))
|
||||
(bundle / 'main.py').unlink()
|
||||
(bundle / 'main.py').symlink_to(src / 'main.py')
|
||||
with self.assertRaises(ValueError):
|
||||
verify_forecast_bundle(bundle, src)
|
||||
|
||||
def test_models_secrets_and_databases_not_staged(self):
|
||||
with TemporaryDirectory() as d:
|
||||
src, bundle = self.fixture(Path(d))
|
||||
for name in ('.env', 'model.pkl', 'users.db'):
|
||||
(src / name).write_text('not a source file')
|
||||
self.assertEqual(len(verify_forecast_bundle(bundle, src)), 3)
|
||||
|
||||
def test_forecast_checks_have_no_network_or_live_mounts(self):
|
||||
args = next(args for name,args,cwd,timeout in preflight.command_list('p') if name=='forecast_candidate_python311')
|
||||
self.assertEqual(args[args.index('--network')+1], 'none')
|
||||
self.assertEqual(args[args.index('--user')+1], '1000:1000')
|
||||
self.assertIn('--read-only', args)
|
||||
for forbidden in ('--volume', '-v', '--env-file', '--privileged', 'up', 'restart'):
|
||||
self.assertNotIn(forbidden, args)
|
||||
|
||||
def test_runner_does_not_import_application_main(self):
|
||||
source = (ROOT / 'acceptance/check_forecast.py').read_text()
|
||||
tree = ast.parse(source)
|
||||
imports = {a.name for node in ast.walk(tree) if isinstance(node, ast.Import) for a in node.names}
|
||||
self.assertNotIn('main', imports)
|
||||
self.assertNotIn('run_forecast(', source)
|
||||
self.assertNotIn('get_configs(', source)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user