Skip to content

Commit 2f5a800

Browse files
committed
test: add integration test for repo lifecycle against production
Tests the full CRUD lifecycle: create repo → list → get → create VM → commit → create tag → list tags → get tag → update tag → delete tag → delete repo Cleans up all resources (repo + VM) via t.Cleanup regardless of test outcome. Uses unique repo names to avoid collisions.
1 parent 811b0fe commit 2f5a800

1 file changed

Lines changed: 212 additions & 0 deletions

File tree

Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
package test
2+
3+
import (
4+
"strings"
5+
"testing"
6+
7+
"github.com/hdresearch/vers-cli/test/testutil"
8+
)
9+
10+
// TestRepoLifecycle exercises the full repo CRUD lifecycle against production:
11+
// create repo → list → get → create tag → list tags → get tag → update tag → delete tag → delete repo.
12+
// Everything is cleaned up regardless of test outcome.
13+
func TestRepoLifecycle(t *testing.T) {
14+
testutil.TestEnv(t)
15+
testutil.EnsureBuilt(t)
16+
17+
repoName := testutil.UniqueAlias("repo")
18+
19+
// Always clean up the repo at end, even if something fails midway.
20+
t.Cleanup(func() {
21+
// Best-effort delete; ignore errors (repo may already be deleted).
22+
testutil.RunVers(t, testutil.DefaultTimeout, "repo", "delete", repoName)
23+
})
24+
25+
// ── Create repo ──────────────────────────────────────────────
26+
out, err := testutil.RunVers(t, testutil.DefaultTimeout,
27+
"repo", "create", repoName, "-d", "integration test repo")
28+
if err != nil {
29+
t.Fatalf("repo create failed: %v\nOutput:\n%s", err, out)
30+
}
31+
if !strings.Contains(out, repoName) {
32+
t.Fatalf("expected repo name in output, got:\n%s", out)
33+
}
34+
35+
// ── List repos ───────────────────────────────────────────────
36+
out, err = testutil.RunVers(t, testutil.DefaultTimeout, "repo", "list")
37+
if err != nil {
38+
t.Fatalf("repo list failed: %v\nOutput:\n%s", err, out)
39+
}
40+
if !strings.Contains(out, repoName) {
41+
t.Fatalf("expected %s in list output, got:\n%s", repoName, out)
42+
}
43+
44+
// ── List repos (quiet) ───────────────────────────────────────
45+
out, err = testutil.RunVers(t, testutil.DefaultTimeout, "repo", "list", "-q")
46+
if err != nil {
47+
t.Fatalf("repo list -q failed: %v\nOutput:\n%s", err, out)
48+
}
49+
found := false
50+
for _, line := range strings.Split(strings.TrimSpace(out), "\n") {
51+
if strings.TrimSpace(line) == repoName {
52+
found = true
53+
break
54+
}
55+
}
56+
if !found {
57+
t.Fatalf("expected %s in quiet list output, got:\n%s", repoName, out)
58+
}
59+
60+
// ── List repos (json) ────────────────────────────────────────
61+
out, err = testutil.RunVers(t, testutil.DefaultTimeout, "repo", "list", "--format", "json")
62+
if err != nil {
63+
t.Fatalf("repo list --format json failed: %v\nOutput:\n%s", err, out)
64+
}
65+
if !strings.Contains(out, repoName) {
66+
t.Fatalf("expected %s in json output, got:\n%s", repoName, out)
67+
}
68+
69+
// ── Get repo ─────────────────────────────────────────────────
70+
out, err = testutil.RunVers(t, testutil.DefaultTimeout, "repo", "get", repoName)
71+
if err != nil {
72+
t.Fatalf("repo get failed: %v\nOutput:\n%s", err, out)
73+
}
74+
if !strings.Contains(out, repoName) {
75+
t.Fatalf("expected %s in get output, got:\n%s", repoName, out)
76+
}
77+
if !strings.Contains(out, "integration test repo") {
78+
t.Fatalf("expected description in get output, got:\n%s", out)
79+
}
80+
81+
// ── We need a commit to create a tag. Create a VM, commit it, clean up. ──
82+
out, err = testutil.RunVers(t, testutil.DefaultTimeout, "run")
83+
if err != nil {
84+
t.Fatalf("vers run failed: %v\nOutput:\n%s", err, out)
85+
}
86+
vmID, err := testutil.ParseVMID(out)
87+
if err != nil {
88+
t.Fatalf("failed to parse VM ID: %v\nOutput:\n%s", err, out)
89+
}
90+
testutil.RegisterVMCleanup(t, vmID, true)
91+
92+
out, err = testutil.RunVers(t, testutil.DefaultTimeout, "commit", "create", vmID)
93+
if err != nil {
94+
t.Fatalf("commit create failed: %v\nOutput:\n%s", err, out)
95+
}
96+
commitID := parseCommitID(t, out)
97+
98+
// ── Create tag in repo ───────────────────────────────────────
99+
out, err = testutil.RunVers(t, testutil.DefaultTimeout,
100+
"repo", "tag", "create", repoName, "v1", commitID, "-d", "first release")
101+
if err != nil {
102+
t.Fatalf("repo tag create failed: %v\nOutput:\n%s", err, out)
103+
}
104+
if !strings.Contains(out, repoName+":v1") {
105+
t.Fatalf("expected reference %s:v1 in output, got:\n%s", repoName, out)
106+
}
107+
108+
// ── List tags ────────────────────────────────────────────────
109+
out, err = testutil.RunVers(t, testutil.DefaultTimeout,
110+
"repo", "tag", "list", repoName)
111+
if err != nil {
112+
t.Fatalf("repo tag list failed: %v\nOutput:\n%s", err, out)
113+
}
114+
if !strings.Contains(out, "v1") {
115+
t.Fatalf("expected v1 in tag list output, got:\n%s", out)
116+
}
117+
118+
// ── List tags (quiet) ────────────────────────────────────────
119+
out, err = testutil.RunVers(t, testutil.DefaultTimeout,
120+
"repo", "tag", "list", repoName, "-q")
121+
if err != nil {
122+
t.Fatalf("repo tag list -q failed: %v\nOutput:\n%s", err, out)
123+
}
124+
if strings.TrimSpace(out) != "v1" {
125+
t.Fatalf("expected 'v1' in quiet tag list, got:\n%s", out)
126+
}
127+
128+
// ── Get tag ──────────────────────────────────────────────────
129+
out, err = testutil.RunVers(t, testutil.DefaultTimeout,
130+
"repo", "tag", "get", repoName, "v1")
131+
if err != nil {
132+
t.Fatalf("repo tag get failed: %v\nOutput:\n%s", err, out)
133+
}
134+
if !strings.Contains(out, commitID) {
135+
t.Fatalf("expected commit ID in tag get output, got:\n%s", out)
136+
}
137+
if !strings.Contains(out, "first release") {
138+
t.Fatalf("expected description in tag get output, got:\n%s", out)
139+
}
140+
141+
// ── Update tag description ───────────────────────────────────
142+
out, err = testutil.RunVers(t, testutil.DefaultTimeout,
143+
"repo", "tag", "update", repoName, "v1", "-d", "updated release")
144+
if err != nil {
145+
t.Fatalf("repo tag update failed: %v\nOutput:\n%s", err, out)
146+
}
147+
148+
// ── Delete tag ───────────────────────────────────────────────
149+
out, err = testutil.RunVers(t, testutil.DefaultTimeout,
150+
"repo", "tag", "delete", repoName, "v1")
151+
if err != nil {
152+
t.Fatalf("repo tag delete failed: %v\nOutput:\n%s", err, out)
153+
}
154+
if !strings.Contains(out, "deleted") {
155+
t.Fatalf("expected 'deleted' in output, got:\n%s", out)
156+
}
157+
158+
// ── Verify tag is gone ───────────────────────────────────────
159+
out, err = testutil.RunVers(t, testutil.DefaultTimeout,
160+
"repo", "tag", "list", repoName)
161+
if err != nil {
162+
t.Fatalf("repo tag list after delete failed: %v\nOutput:\n%s", err, out)
163+
}
164+
if strings.Contains(out, "v1") && !strings.Contains(out, "No tags found") {
165+
t.Fatalf("expected v1 to be gone from tag list, got:\n%s", out)
166+
}
167+
168+
// ── Delete repo ──────────────────────────────────────────────
169+
out, err = testutil.RunVers(t, testutil.DefaultTimeout, "repo", "delete", repoName)
170+
if err != nil {
171+
t.Fatalf("repo delete failed: %v\nOutput:\n%s", err, out)
172+
}
173+
if !strings.Contains(out, "deleted") {
174+
t.Fatalf("expected 'deleted' in output, got:\n%s", out)
175+
}
176+
177+
// ── Verify repo is gone ──────────────────────────────────────
178+
out, err = testutil.RunVers(t, testutil.DefaultTimeout, "repo", "list", "-q")
179+
if err != nil {
180+
t.Fatalf("repo list after delete failed: %v\nOutput:\n%s", err, out)
181+
}
182+
if strings.Contains(out, repoName) {
183+
t.Fatalf("expected %s to be gone from list, got:\n%s", repoName, out)
184+
}
185+
}
186+
187+
// parseCommitID extracts a commit ID from `vers commit create` output.
188+
// Expected format:
189+
//
190+
// ✓ Committed VM '<vm-id>'
191+
// Commit ID: <commit-id>
192+
func parseCommitID(t *testing.T, output string) string {
193+
t.Helper()
194+
for _, line := range strings.Split(output, "\n") {
195+
line = strings.TrimSpace(line)
196+
if strings.HasPrefix(line, "Commit ID:") {
197+
parts := strings.SplitN(line, ":", 2)
198+
if len(parts) == 2 {
199+
id := strings.TrimSpace(parts[1])
200+
if id != "" {
201+
return id
202+
}
203+
}
204+
}
205+
// Fallback: a bare UUID on its own line
206+
if len(line) == 36 && strings.Count(line, "-") == 4 {
207+
return line
208+
}
209+
}
210+
t.Fatalf("could not parse commit ID from output:\n%s", output)
211+
return ""
212+
}

0 commit comments

Comments
 (0)