arche / internal/wc/wc_test.go

commit 154431fd
  1package wc_test
  2
  3import (
  4	"os"
  5	"path/filepath"
  6	"testing"
  7
  8	"arche/internal/repo"
  9	"arche/internal/wc"
 10
 11	_ "github.com/mattn/go-sqlite3"
 12)
 13
 14func TestMaterialize_RestoresFiles(t *testing.T) {
 15	r := initRepo(t)
 16	w := wc.New(r)
 17
 18	writeFile(t, r, "keep.txt", "original")
 19	writeFile(t, r, "also.txt", "also original")
 20
 21	snapped, _, err := w.Snap("baseline")
 22	if err != nil {
 23		t.Fatalf("Snap: %v", err)
 24	}
 25
 26	baselineTree := snapped.TreeID
 27	headCID, err := r.HeadChangeID()
 28	if err != nil {
 29		t.Fatalf("HeadChangeID: %v", err)
 30	}
 31
 32	writeFile(t, r, "keep.txt", "MODIFIED")
 33	writeFile(t, r, "extra.txt", "should vanish")
 34
 35	if err := w.Materialize(baselineTree, headCID); err != nil {
 36		t.Fatalf("Materialize: %v", err)
 37	}
 38
 39	got, exists := readFile(t, r, "keep.txt")
 40	if !exists || got != "original" {
 41		t.Errorf("keep.txt: want %q got (%q, exists=%v)", "original", got, exists)
 42	}
 43
 44	got2, exists2 := readFile(t, r, "also.txt")
 45	if !exists2 || got2 != "also original" {
 46		t.Errorf("also.txt: want %q got (%q, exists=%v)", "also original", got2, exists2)
 47	}
 48
 49	_, extraExists := readFile(t, r, "extra.txt")
 50	if extraExists {
 51		t.Error("extra.txt should have been removed by Materialize")
 52	}
 53}
 54
 55func TestMaterialize_AddsNewFiles(t *testing.T) {
 56	r := initRepo(t)
 57	w := wc.New(r)
 58
 59	writeFile(t, r, "new.txt", "new content")
 60	snapped, _, err := w.Snap("add new.txt")
 61	if err != nil {
 62		t.Fatalf("Snap: %v", err)
 63	}
 64
 65	targetTree := snapped.TreeID
 66	headCID, _ := r.HeadChangeID()
 67
 68	if err := os.Remove(filepath.Join(r.Root, "new.txt")); err != nil {
 69		t.Fatalf("Remove: %v", err)
 70	}
 71
 72	_, existsBefore := readFile(t, r, "new.txt")
 73	if existsBefore {
 74		t.Fatal("pre-condition: new.txt should not exist")
 75	}
 76
 77	if err := w.Materialize(targetTree, headCID); err != nil {
 78		t.Fatalf("Materialize: %v", err)
 79	}
 80
 81	got, exists := readFile(t, r, "new.txt")
 82	if !exists || got != "new content" {
 83		t.Errorf("new.txt: want %q got (%q, exists=%v)", "new content", got, exists)
 84	}
 85}
 86
 87func TestMaterialize_RecordsOperation(t *testing.T) {
 88	r := initRepo(t)
 89	w := wc.New(r)
 90
 91	writeFile(t, r, "f.txt", "data")
 92	snapped, _, err := w.Snap("snap")
 93	if err != nil {
 94		t.Fatalf("Snap: %v", err)
 95	}
 96
 97	headCID, _ := r.HeadChangeID()
 98	opsBefore, _ := r.Store.ListOperations(50)
 99
100	if err := w.Materialize(snapped.TreeID, headCID); err != nil {
101		t.Fatalf("Materialize: %v", err)
102	}
103
104	opsAfter, _ := r.Store.ListOperations(50)
105	if len(opsAfter) <= len(opsBefore) {
106		t.Errorf("expected a new operation after Materialize; before=%d after=%d",
107			len(opsBefore), len(opsAfter))
108	}
109
110	last, _ := r.Store.GetLastOperation()
111	if last == nil || last.Kind != "co" {
112		t.Errorf("last operation kind: want co, got %v", last)
113	}
114}
115
116func TestStatus_Modified(t *testing.T) {
117	r := initRepo(t)
118	w := wc.New(r)
119
120	writeFile(t, r, "mod.txt", "before")
121
122	if _, _, err := w.Snap("base"); err != nil {
123		t.Fatalf("Snap: %v", err)
124	}
125
126	writeFile(t, r, "mod.txt", "after")
127
128	statuses, err := w.Status()
129	if err != nil {
130		t.Fatalf("Status: %v", err)
131	}
132
133	var found bool
134	for _, s := range statuses {
135		if s.Path == "mod.txt" && s.Status == 'M' {
136			found = true
137		}
138	}
139	if !found {
140		t.Errorf("mod.txt should be Modified, got statuses: %v", statuses)
141	}
142}
143
144func TestStatus_Deleted(t *testing.T) {
145	r := initRepo(t)
146	w := wc.New(r)
147
148	writeFile(t, r, "del.txt", "bye")
149
150	if _, _, err := w.Snap("base"); err != nil {
151		t.Fatalf("Snap: %v", err)
152	}
153
154	if err := os.Remove(filepath.Join(r.Root, "del.txt")); err != nil {
155		t.Fatalf("Remove: %v", err)
156	}
157
158	statuses, err := w.Status()
159	if err != nil {
160		t.Fatalf("Status: %v", err)
161	}
162
163	var found bool
164	for _, s := range statuses {
165		if s.Path == "del.txt" && s.Status == 'D' {
166			found = true
167		}
168	}
169	if !found {
170		t.Errorf("del.txt should be Deleted, got statuses: %v", statuses)
171	}
172}
173
174func initRepo(t *testing.T) *repo.Repo {
175	t.Helper()
176	dir := t.TempDir()
177	r, err := repo.Init(dir)
178	if err != nil {
179		t.Fatalf("repo.Init: %v", err)
180	}
181	t.Cleanup(func() { r.Close() })
182	return r
183}
184
185func writeFile(t *testing.T, r *repo.Repo, name, content string) {
186	t.Helper()
187	abs := filepath.Join(r.Root, name)
188	if err := os.MkdirAll(filepath.Dir(abs), 0o755); err != nil {
189		t.Fatalf("MkdirAll: %v", err)
190	}
191	if err := os.WriteFile(abs, []byte(content), 0o644); err != nil {
192		t.Fatalf("WriteFile %s: %v", name, err)
193	}
194}
195
196func readFile(t *testing.T, r *repo.Repo, name string) (string, bool) {
197	t.Helper()
198	data, err := os.ReadFile(filepath.Join(r.Root, name))
199	if os.IsNotExist(err) {
200		return "", false
201	}
202	if err != nil {
203		t.Fatalf("readFile %s: %v", name, err)
204	}
205	return string(data), true
206}