module cli import os import compress.zlib import crypto.sha256 import json2 import src.exchange import src.release import src.remote import src.events import src.history import src.snapshot import src.storage fn test_doctor_checks_cycles_and_missing_heads_without_walking_forever() { saves := [history.Save{ id: 'aaaaaaaaaaaa', parent: 'bbbbbbbbbbbb' }, history.Save{ id: 'bbbbbbbbbbbb', parent: 'aaaaaaaaaaaa' }] assert doctor_history(saves, 'cccccccccccc').any(it.contains('head')) assert doctor_history(saves, 'aaaaaaaaaaaa').any(it.contains('cycle')) assert doctor_history([history.Save{ id: 'aaaaaaaaaaaa', parent: 'cccccccccccc' }], 'aaaaaaaaaaaa').any(it.contains('missing parent')) assert doctor_history([]history.Save{}, '').len == 0 assert doctor_history([history.Save{ id: 'aaaaaaaaaaaa' }], '').any(it.contains('no head')) } fn test_doctor_checks_off_line_paths_and_file_directory_collisions() { mut report := DoctorJson{} doctor_files('aaaaaaaaaaaa', [snapshot.FileEntry{ path: '../unsafe' }, snapshot.FileEntry{ path: 'a' }, snapshot.FileEntry{ path: 'a/b' }], []events.Event{}, mut report) assert report.problems.any(it.contains('unsafe path')) assert report.problems.any(it.contains('file and a directory')) } fn test_doctor_reports_unsafe_save_paths_with_a_quoted_strip_command() { dir := os.join_path(os.temp_dir(), 'koh3_doctor_paths_${os.getpid()}') os.rmdir_all(dir) or {} os.mkdir_all(dir)! defer { os.rmdir_all(dir) or {} } store := storage.create(dir)! defer { store.close() } hash := store.put_object('unsafe'.bytes())! store.begin()! history.import_save(store, history.Save{ id: 'badbadbadbad' }, [snapshot.FileEntry{ path: "../June's file" hash: hash }])! history.set_head(store, 'badbadbadbad')! store.end()! mut report := DoctorJson{} doctor_files('badbadbadbad', history.files_of(store, 'badbadbadbad')!, []events.Event{}, mut report) assert report.problems == [ "save [badbadbadbad] has unsafe path ../June's file — koh strip '../June'\\''s file'", ] } struct DoctorReadOnly { mut: items map[string][]u8 } fn (mut backend DoctorReadOnly) get(key string) !remote.Object { if body := backend.items[key] { return remote.Object{ found: true, body: body } } return remote.Object{} } fn (mut backend DoctorReadOnly) list(prefix string) ![]string { return backend.items.keys().filter(it.starts_with('${prefix}/')) } fn (mut backend DoctorReadOnly) put_if_absent(key string, body []u8) !bool { panic('Doctor attempted a remote write') } fn (mut backend DoctorReadOnly) put_if_match(key string, body []u8, tag string) !bool { panic('Doctor attempted a remote write') } fn (mut backend DoctorReadOnly) delete(key string) ! { panic('Doctor attempted a remote delete') } fn test_doctor_remote_audits_unreferenced_objects_and_release_checksums_without_writes() { data := 'file'.bytes() hash := storage.hash_hex(data) mut backend := DoctorReadOnly{ items: { 'repo/head': 'aaaaaaaaaaaa'.bytes() 'repo/saves/aaaaaaaaaaaa': json2.encode(exchange.SaveRecord{ id: 'aaaaaaaaaaaa' files: [exchange.FileRecord{ path: 'file', hash: hash }] }).bytes() 'repo/objects/${hash}': zlib.compress(data)! 'repo/releases/latest': '1.0.0'.bytes() 'repo/releases/1.0.0/release.json': json2.encode(release.Record{ repo: 'repo' version: '1.0.0' save: 'aaaaaaaaaaaa' files: [release.File{ platform: 'linux-aarch64' name: 'app' size: data.len blake3: hash sha256: sha256.sum256(data).hex() }] }).bytes() 'repo/releases/1.0.0/linux-aarch64/app': data } } mut report := DoctorJson{ head_id: 'aaaaaaaaaaaa' } doctor_remote_content(mut backend, 'repo', mut report)! assert report.problems.len == 0 assert report.remote_saves == 1 assert report.remote_releases == 1 backend.items['repo/objects/${storage.hash_hex('unreferenced'.bytes())}'] = 'bad zlib'.bytes() backend.items['repo/releases/1.0.0/linux-aarch64/app'] = 'corrupt'.bytes() mut damaged := DoctorJson{} doctor_remote_content(mut backend, 'repo', mut damaged)! assert damaged.problems.any(it.contains('damaged remote object')) assert damaged.problems.any(it.contains('checksum verification')) } fn test_doctor_reports_event_errors_strip_residue_and_flags() { mut report := DoctorJson{} saves := [history.Save{ id: 'aaaaaaaaaaaa' }] event_list := [events.Event{ id: 'strip', kind: 'strip', path: 'private' }, events.Event{ id: 'flag', kind: 'flag', save_id: 'aaaaaaaaaaaa' }, events.Event{ id: 'bad', kind: 'unknown' }] doctor_events(event_list, saves, mut report) doctor_files(saves[0].id, [snapshot.FileEntry{ path: 'private/file' }], event_list, mut report) assert report.flagged_saves == 1 assert report.problems.any(it.contains('unknown kind')) assert report.problems.any(it.contains('stripped path')) }