git/dew/main
tests/cli_test.go
114 lines3 KB last change raw · history
package tests

import (
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"testing"
)

// buildDew compiles the cli once so the tests can drive it as a real process
// instead of reaching into the internals.
func buildDew(t *testing.T) string {
	t.Helper()

	bin := filepath.Join(t.TempDir(), "dew")
	cmd := exec.Command("go", "build", "-o", bin, "../cmd/dew")
	if out, err := cmd.CombinedOutput(); err != nil {
		t.Fatalf("building dew: %v\n%s", err, out)
	}
	return bin
}

// newHome writes a throwaway config pointing at temp files and returns the
// HOME to run under, so the tests never touch the real config.
func newHome(t *testing.T) (home, todo, done string) {
	t.Helper()

	dir := t.TempDir()
	home = t.TempDir()
	todo = filepath.Join(dir, "todo.txt")
	done = filepath.Join(dir, "done.txt")

	cfgDir := filepath.Join(home, ".config", "dew")
	if err := os.MkdirAll(cfgDir, 0755); err != nil {
		t.Fatalf("creating config dir: %v", err)
	}

	cfg := fmt.Sprintf("{\"todo_file\": %q, \"done_file\": %q}", todo, done)
	if err := os.WriteFile(filepath.Join(cfgDir, "config.json"), []byte(cfg), 0644); err != nil {
		t.Fatalf("writing config: %v", err)
	}
	return home, todo, done
}

func runDew(t *testing.T, bin, home string, args ...string) (string, error) {
	t.Helper()

	env := []string{}
	for _, e := range os.Environ() {
		if !strings.HasPrefix(e, "HOME=") {
			env = append(env, e)
		}
	}

	cmd := exec.Command(bin, args...)
	cmd.Env = append(env, "HOME="+home)
	out, err := cmd.CombinedOutput()
	return string(out), err
}

func TestCLIAddAndList(t *testing.T) {
	bin := buildDew(t)
	home, _, _ := newHome(t)

	out, err := runDew(t, bin, home, "add", "buy", "catnip", "+metsa", "@chores")
	if err != nil {
		t.Fatalf("dew add: %v\n%s", err, out)
	}
	if !strings.Contains(out, "added task: buy catnip +metsa @chores") {
		t.Errorf("add output = %q", out)
	}

	out, err = runDew(t, bin, home, "ls")
	if err != nil {
		t.Fatalf("dew ls: %v\n%s", err, out)
	}
	if !strings.Contains(out, "buy catnip") || !strings.Contains(out, "metsa") {
		t.Errorf("ls output missing the task: %q", out)
	}
}

func TestCLIDoneArchives(t *testing.T) {
	bin := buildDew(t)
	home, todo, done := newHome(t)

	for _, desc := range []string{"first task +home", "second task +home"} {
		if out, err := runDew(t, bin, home, "add", desc); err != nil {
			t.Fatalf("dew add %q: %v\n%s", desc, err, out)
		}
	}

	if out, err := runDew(t, bin, home, "do", "1"); err != nil {
		t.Fatalf("dew do 1: %v\n%s", err, out)
	}

	todoBytes, err := os.ReadFile(todo)
	if err != nil {
		t.Fatalf("reading todo: %v", err)
	}
	if got := strings.TrimSpace(string(todoBytes)); got != "second task +home" {
		t.Errorf("todo = %q, want just the second task", got)
	}

	doneBytes, err := os.ReadFile(done)
	if err != nil {
		t.Fatalf("reading done: %v", err)
	}
	if !strings.Contains(string(doneBytes), "first task +home") {
		t.Errorf("done.txt = %q, want the archived first task", doneBytes)
	}
}