This change adds `_ =` assignments to exec.Command(...).Run() calls in test setup code to explicitly ignore error returns, likely to satisfy linters or static analysis tools without changing behavior.
475 lines
13 KiB
Go
475 lines
13 KiB
Go
package workon
|
|
|
|
import (
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestRunRejectsEmptyTitle(t *testing.T) {
|
|
err := Run("", "", false, false)
|
|
if err == nil {
|
|
t.Fatal("expected error for empty title, got nil")
|
|
}
|
|
if !strings.Contains(err.Error(), "todo_item_title is required") {
|
|
t.Errorf("unexpected error message: %s", err.Error())
|
|
}
|
|
}
|
|
|
|
func TestMoveQueuedToDoing(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
origDir, _ := os.Getwd()
|
|
if err := os.Chdir(tmpDir); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer func() { _ = os.Chdir(origDir) }()
|
|
|
|
// Set up todo structure
|
|
if err := os.MkdirAll("todo/queued", 0755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.MkdirAll("todo/doing", 0755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.WriteFile("todo/queued/test-item.md", []byte("# Test Item\n"), 0644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if err := moveQueuedToDoing("test-item"); err != nil {
|
|
t.Fatalf("moveQueuedToDoing failed: %v", err)
|
|
}
|
|
|
|
// Verify source is gone
|
|
if _, err := os.Stat("todo/queued/test-item.md"); !os.IsNotExist(err) {
|
|
t.Error("expected queued file to be removed")
|
|
}
|
|
// Verify destination exists
|
|
if _, err := os.Stat("todo/doing/test-item.md"); err != nil {
|
|
t.Errorf("expected doing file to exist: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestMoveQueuedToDoingMissingFile(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
origDir, _ := os.Getwd()
|
|
if err := os.Chdir(tmpDir); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer func() { _ = os.Chdir(origDir) }()
|
|
|
|
if err := os.MkdirAll("todo/queued", 0755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.MkdirAll("todo/doing", 0755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
err := moveQueuedToDoing("nonexistent")
|
|
if err == nil {
|
|
t.Fatal("expected error for missing queued file, got nil")
|
|
}
|
|
}
|
|
|
|
func TestCreateFixFile(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
path := filepath.Join(tmpDir, "my-fix.md")
|
|
|
|
if err := createFixFile(path, "my-fix"); err != nil {
|
|
t.Fatalf("createFixFile failed: %v", err)
|
|
}
|
|
|
|
data, err := os.ReadFile(path)
|
|
if err != nil {
|
|
t.Fatalf("failed to read created file: %v", err)
|
|
}
|
|
|
|
content := string(data)
|
|
if !strings.Contains(content, "# my-fix") {
|
|
t.Errorf("expected title heading, got: %s", content)
|
|
}
|
|
if !strings.Contains(content, "## Work Plan") {
|
|
t.Errorf("expected Work Plan heading, got: %s", content)
|
|
}
|
|
}
|
|
|
|
func TestReadFileContent(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
|
|
t.Run("existing file", func(t *testing.T) {
|
|
path := filepath.Join(tmpDir, "exists.md")
|
|
expected := "hello world"
|
|
if err := os.WriteFile(path, []byte(expected), 0644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
got := readFileContent(path)
|
|
if got != expected {
|
|
t.Errorf("readFileContent = %q, want %q", got, expected)
|
|
}
|
|
})
|
|
|
|
t.Run("missing file returns empty string", func(t *testing.T) {
|
|
got := readFileContent(filepath.Join(tmpDir, "missing.md"))
|
|
if got != "" {
|
|
t.Errorf("readFileContent for missing file = %q, want empty", got)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestCompleteItemMovesFile(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
origDir, _ := os.Getwd()
|
|
if err := os.Chdir(tmpDir); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer func() { _ = os.Chdir(origDir) }()
|
|
|
|
// Set up todo structure with a doing item
|
|
if err := os.MkdirAll("todo/doing", 0755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.MkdirAll("todo/completed", 0755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.WriteFile("todo/doing/test-item.md", []byte("# Test\n"), 0644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// completeItem will try to git commit — that will fail in a non-git temp dir,
|
|
// but we can verify the file move happened before the commit step
|
|
_ = completeItem("test-item", true)
|
|
|
|
// For a fix, file should be moved regardless of commit outcome
|
|
if _, err := os.Stat("todo/completed/test-item.md"); err != nil {
|
|
t.Errorf("expected completed file to exist: %v", err)
|
|
}
|
|
if _, err := os.Stat("todo/doing/test-item.md"); !os.IsNotExist(err) {
|
|
t.Error("expected doing file to be removed")
|
|
}
|
|
}
|
|
|
|
func TestUpdateReadmeIndex(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
origDir, _ := os.Getwd()
|
|
if err := os.Chdir(tmpDir); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer func() { _ = os.Chdir(origDir) }()
|
|
|
|
if err := os.MkdirAll("todo", 0755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
readme := `# Todo Index
|
|
|
|
## Queued
|
|
|
|
* [1] [my-feature](queued/my-feature.md): My feature
|
|
* [2] [other-item](queued/other-item.md): Other item
|
|
|
|
## Completed
|
|
|
|
* [old-item](./completed/old-item.md) : old-item *(done)*
|
|
`
|
|
if err := os.WriteFile("todo/README.md", []byte(readme), 0644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if err := updateReadmeIndex("my-feature"); err != nil {
|
|
t.Fatalf("updateReadmeIndex failed: %v", err)
|
|
}
|
|
|
|
data, err := os.ReadFile("todo/README.md")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
result := string(data)
|
|
|
|
// Should have removed the queued entry
|
|
if strings.Contains(result, "queued/my-feature.md") {
|
|
t.Error("expected my-feature to be removed from Queued section")
|
|
}
|
|
|
|
// Should still have the other queued item
|
|
if !strings.Contains(result, "queued/other-item.md") {
|
|
t.Error("expected other-item to remain in Queued section")
|
|
}
|
|
|
|
// Should have added to Completed section
|
|
if !strings.Contains(result, "completed/my-feature.md") {
|
|
t.Error("expected my-feature to appear in Completed section")
|
|
}
|
|
|
|
// Old completed item should still be there
|
|
if !strings.Contains(result, "completed/old-item.md") {
|
|
t.Error("expected old-item to remain in Completed section")
|
|
}
|
|
}
|
|
|
|
func TestUpdateReadmeIndexMissingReadme(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
origDir, _ := os.Getwd()
|
|
if err := os.Chdir(tmpDir); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer func() { _ = os.Chdir(origDir) }()
|
|
|
|
err := updateReadmeIndex("anything")
|
|
if err == nil {
|
|
t.Fatal("expected error when README doesn't exist, got nil")
|
|
}
|
|
}
|
|
|
|
// initTempGitRepo creates a temp dir with a git repo and returns it.
|
|
// The caller is responsible for chdir-ing back.
|
|
func initTempGitRepo(t *testing.T) string {
|
|
t.Helper()
|
|
tmpDir := t.TempDir()
|
|
cmds := [][]string{
|
|
{"git", "init"},
|
|
{"git", "config", "user.email", "test@test.com"},
|
|
{"git", "config", "user.name", "Test"},
|
|
}
|
|
for _, args := range cmds {
|
|
cmd := exec.Command(args[0], args[1:]...)
|
|
cmd.Dir = tmpDir
|
|
if out, err := cmd.CombinedOutput(); err != nil {
|
|
t.Fatalf("git init setup failed (%v): %s", err, out)
|
|
}
|
|
}
|
|
return tmpDir
|
|
}
|
|
|
|
func TestCreateGitBranchNew(t *testing.T) {
|
|
tmpDir := initTempGitRepo(t)
|
|
origDir, _ := os.Getwd()
|
|
if err := os.Chdir(tmpDir); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer func() { _ = os.Chdir(origDir) }()
|
|
|
|
// Need at least one commit for branches to work
|
|
if err := os.WriteFile("init.txt", []byte("init"), 0644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
_ = exec.Command("git", "add", ".").Run()
|
|
_ = exec.Command("git", "commit", "-m", "initial").Run()
|
|
|
|
if err := createGitBranch("feature/test-branch"); err != nil {
|
|
t.Fatalf("createGitBranch failed: %v", err)
|
|
}
|
|
|
|
// Verify we're on the new branch
|
|
out, err := exec.Command("git", "branch", "--show-current").Output()
|
|
if err != nil {
|
|
t.Fatalf("git branch --show-current failed: %v", err)
|
|
}
|
|
branch := strings.TrimSpace(string(out))
|
|
if branch != "feature/test-branch" {
|
|
t.Errorf("expected branch 'feature/test-branch', got %q", branch)
|
|
}
|
|
}
|
|
|
|
func TestCreateGitBranchExisting(t *testing.T) {
|
|
tmpDir := initTempGitRepo(t)
|
|
origDir, _ := os.Getwd()
|
|
if err := os.Chdir(tmpDir); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer func() { _ = os.Chdir(origDir) }()
|
|
|
|
// Initial commit + create branch
|
|
if err := os.WriteFile("init.txt", []byte("init"), 0644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
_ = exec.Command("git", "add", ".").Run()
|
|
_ = exec.Command("git", "commit", "-m", "initial").Run()
|
|
_ = exec.Command("git", "checkout", "-b", "fix/existing").Run()
|
|
_ = exec.Command("git", "checkout", "-").Run() // back to main/master
|
|
|
|
// Should checkout existing branch, not error
|
|
if err := createGitBranch("fix/existing"); err != nil {
|
|
t.Fatalf("createGitBranch for existing branch failed: %v", err)
|
|
}
|
|
|
|
out, _ := exec.Command("git", "branch", "--show-current").Output()
|
|
branch := strings.TrimSpace(string(out))
|
|
if branch != "fix/existing" {
|
|
t.Errorf("expected branch 'fix/existing', got %q", branch)
|
|
}
|
|
}
|
|
|
|
func TestCommitChangesInGitRepo(t *testing.T) {
|
|
tmpDir := initTempGitRepo(t)
|
|
origDir, _ := os.Getwd()
|
|
if err := os.Chdir(tmpDir); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer func() { _ = os.Chdir(origDir) }()
|
|
|
|
// Initial commit
|
|
if err := os.WriteFile("init.txt", []byte("init"), 0644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
_ = exec.Command("git", "add", ".").Run()
|
|
_ = exec.Command("git", "commit", "-m", "initial").Run()
|
|
|
|
// Create a todo file to commit
|
|
if err := os.MkdirAll("todo/doing", 0755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.WriteFile("todo/doing/test.md", []byte("# Test\n"), 0644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if err := commitChanges("test commit message"); err != nil {
|
|
t.Fatalf("commitChanges failed: %v", err)
|
|
}
|
|
|
|
// Verify the commit was created
|
|
out, err := exec.Command("git", "log", "--oneline", "-1").Output()
|
|
if err != nil {
|
|
t.Fatalf("git log failed: %v", err)
|
|
}
|
|
if !strings.Contains(string(out), "test commit message") {
|
|
t.Errorf("expected commit message in log, got: %s", out)
|
|
}
|
|
}
|
|
|
|
func TestCommitChangesNothingToCommit(t *testing.T) {
|
|
tmpDir := initTempGitRepo(t)
|
|
origDir, _ := os.Getwd()
|
|
if err := os.Chdir(tmpDir); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer func() { _ = os.Chdir(origDir) }()
|
|
|
|
// Initial commit
|
|
if err := os.WriteFile("init.txt", []byte("init"), 0644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
_ = exec.Command("git", "add", ".").Run()
|
|
_ = exec.Command("git", "commit", "-m", "initial").Run()
|
|
|
|
// Create empty todo dir but no files to stage
|
|
if err := os.MkdirAll("todo", 0755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Should fail — nothing to commit
|
|
err := commitChanges("empty commit")
|
|
if err == nil {
|
|
t.Error("expected error when nothing to commit, got nil")
|
|
}
|
|
}
|
|
|
|
func TestCompleteItemWithGit(t *testing.T) {
|
|
tmpDir := initTempGitRepo(t)
|
|
origDir, _ := os.Getwd()
|
|
if err := os.Chdir(tmpDir); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer func() { _ = os.Chdir(origDir) }()
|
|
|
|
// Initial commit
|
|
if err := os.WriteFile("init.txt", []byte("init"), 0644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
_ = exec.Command("git", "add", ".").Run()
|
|
_ = exec.Command("git", "commit", "-m", "initial").Run()
|
|
|
|
// Set up todo structure
|
|
for _, d := range []string{"todo/doing", "todo/completed"} {
|
|
if err := os.MkdirAll(d, 0755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
if err := os.WriteFile("todo/doing/my-fix.md", []byte("# Fix\n"), 0644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// Stage and commit the doing file first so git tracks it
|
|
_ = exec.Command("git", "add", "todo/").Run()
|
|
_ = exec.Command("git", "commit", "-m", "add doing item").Run()
|
|
|
|
// Now complete as a fix (no README update)
|
|
if err := completeItem("my-fix", true); err != nil {
|
|
t.Fatalf("completeItem failed: %v", err)
|
|
}
|
|
|
|
// Verify moved
|
|
if _, err := os.Stat("todo/completed/my-fix.md"); err != nil {
|
|
t.Errorf("expected completed file: %v", err)
|
|
}
|
|
if _, err := os.Stat("todo/doing/my-fix.md"); !os.IsNotExist(err) {
|
|
t.Error("expected doing file to be gone")
|
|
}
|
|
|
|
// Verify commit
|
|
out, _ := exec.Command("git", "log", "--oneline", "-1").Output()
|
|
if !strings.Contains(string(out), "Complete work on my-fix") {
|
|
t.Errorf("expected completion commit, got: %s", out)
|
|
}
|
|
}
|
|
|
|
func TestCompleteItemNonFixUpdatesReadme(t *testing.T) {
|
|
tmpDir := initTempGitRepo(t)
|
|
origDir, _ := os.Getwd()
|
|
if err := os.Chdir(tmpDir); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer func() { _ = os.Chdir(origDir) }()
|
|
|
|
// Initial commit
|
|
if err := os.WriteFile("init.txt", []byte("init"), 0644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
_ = exec.Command("git", "add", ".").Run()
|
|
_ = exec.Command("git", "commit", "-m", "initial").Run()
|
|
|
|
// Set up todo structure with README
|
|
for _, d := range []string{"todo/doing", "todo/completed"} {
|
|
if err := os.MkdirAll(d, 0755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
readme := "# Todo\n\n## Queued\n\n* [my-todo](queued/my-todo.md): My todo\n\n## Completed\n\n"
|
|
if err := os.WriteFile("todo/README.md", []byte(readme), 0644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.WriteFile("todo/doing/my-todo.md", []byte("# My Todo\n"), 0644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
_ = exec.Command("git", "add", "todo/").Run()
|
|
_ = exec.Command("git", "commit", "-m", "add todo item").Run()
|
|
|
|
// Complete as a non-fix (should update README)
|
|
if err := completeItem("my-todo", false); err != nil {
|
|
t.Fatalf("completeItem (non-fix) failed: %v", err)
|
|
}
|
|
|
|
data, err := os.ReadFile("todo/README.md")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
result := string(data)
|
|
|
|
if strings.Contains(result, "queued/my-todo.md") {
|
|
t.Error("expected my-todo removed from Queued")
|
|
}
|
|
if !strings.Contains(result, "completed/my-todo.md") {
|
|
t.Error("expected my-todo added to Completed")
|
|
}
|
|
}
|
|
|
|
func TestRunCnaddIfAvailable(t *testing.T) {
|
|
// Just exercise the function — it should not panic regardless
|
|
// of whether cnadd is installed
|
|
runCnaddIfAvailable("test-item")
|
|
}
|
|
|
|
func TestOpenIDEIfConfigured(t *testing.T) {
|
|
// With no IDE configured (default), this should silently skip
|
|
openIDEIfConfigured()
|
|
}
|