1module history23import os4import src.snapshot5import src.storage67fn temp_repo(name string) !(storage.Store, string) {8 dir := os.join_path(os.temp_dir(), 'koh3_hist_${os.getpid()}_${name}')9 os.rmdir_all(dir) or {}10 os.mkdir_all(dir, os.MkdirParams{})!11 store := storage.create(dir)!12 return store, dir13}1415fn write_file(root string, rel string, text string) ! {16 full := os.join_path(root, rel)17 os.mkdir_all(os.dir(full), os.MkdirParams{})!18 os.write_file(full, text)!19}2021fn test_walk_then_commit() {22 mut store, dir := temp_repo('commit')!23 defer {24 store.close()25 os.rmdir_all(dir) or {}26 }27 write_file(dir, 'a.txt', 'hello\n')!28 write_file(dir, 'sub/b.txt', 'world\n')!29 entries := snapshot.walk(store, dir, snapshot.Ignore{})!30 assert entries.len == 23132 save := commit_worktree(store, dir, snapshot.Ignore{}, SaveInput{33 message: 'first save'34 })!35 assert save.id.len == 1236 assert save.parent == ''37 assert head_id(store) == save.id3839 files := files_of(store, save.id)!40 assert files.len == 241 assert files[0].path == 'a.txt'42 assert files[1].path == 'sub/b.txt'4344 45 if _ := commit_worktree(store, dir, snapshot.Ignore{}, SaveInput{ message: 'again' }) {46 assert false, 'a save with no changes should refuse'47 } else {48 assert err.msg() == nothing_changed49 }50 second := commit_worktree(store, dir, snapshot.Ignore{}, SaveInput{51 message: 'checked anyway'52 is_checkpoint: true53 })!54 assert second.parent == save.id55 assert second.is_checkpoint56}5758fn test_going_back_is_recorded_not_rewound() {59 mut store, dir := temp_repo('back')!60 defer {61 store.close()62 os.rmdir_all(dir) or {}63 }64 write_file(dir, 'a.txt', 'one\n')!65 first := commit_worktree(store, dir, snapshot.Ignore{}, SaveInput{ message: 'one' })!66 write_file(dir, 'a.txt', 'two\n')!67 second := commit_worktree(store, dir, snapshot.Ignore{}, SaveInput{ message: 'two' })!6869 70 assert effective(store)!.id == second.id71 first_files := files_of(store, first.id)!72 restore := commit_files(store, '', first_files, SaveInput{73 message: 'undo: ${first.id}'74 restores: first.id75 })!76 assert restore.parent == second.id77 assert restore.restores == first.id78 assert effective(store)!.id == first.id79 80 81 if _ := step_back(store, effective(store)!.id, 1) {82 assert false, 'there is nothing before the first save'83 } else {84 assert err.msg().contains('nothing before')85 }8687 88 assert same_files(store, head_id(store), first_files)!89}9091fn same_files(store &storage.Store, id string, expected []snapshot.FileEntry) !bool {92 actual := files_of(store, id)!93 if actual.len != expected.len {94 return false95 }96 for i in 0 .. actual.len {97 if actual[i].path != expected[i].path || actual[i].hash != expected[i].hash {98 return false99 }100 }101 return true102}103104fn test_resolve_accepts_a_unique_prefix() {105 mut store, dir := temp_repo('resolve')!106 defer {107 store.close()108 os.rmdir_all(dir) or {}109 }110 write_file(dir, 'a.txt', 'one\n')!111 first := commit_worktree(store, dir, snapshot.Ignore{}, SaveInput{ message: 'one' })!112 assert resolve(store, first.id[..6])!.id == first.id113 if _ := resolve(store, 'abc') {114 assert false, 'a prefix shorter than six characters should be refused'115 } else {116 assert err.msg().contains('at least 6')117 }118 if _ := resolve(store, 'zzzzzz') {119 assert false, 'an unknown prefix should be refused'120 } else {121 assert err.msg().contains('no save starts with')122 }123}