134 lines
3.6 KiB
Go
134 lines
3.6 KiB
Go
package watchreset
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"testing"
|
|
|
|
taskservice "github.com/kandev/kandev/internal/task/service"
|
|
)
|
|
|
|
type fakeResetter struct {
|
|
ids []string
|
|
listErr error
|
|
cleared bool
|
|
clearErr error
|
|
}
|
|
|
|
func (f *fakeResetter) ListTaskIDs(_ context.Context) ([]string, error) {
|
|
return f.ids, f.listErr
|
|
}
|
|
func (f *fakeResetter) Clear(_ context.Context) error {
|
|
f.cleared = true
|
|
return f.clearErr
|
|
}
|
|
|
|
type fakeDeleter struct {
|
|
called []string
|
|
failOn map[string]error
|
|
outcome *taskservice.CascadeOutcome
|
|
}
|
|
|
|
func (f *fakeDeleter) DeleteTaskTree(_ context.Context, id string, _ bool) (*taskservice.CascadeOutcome, error) {
|
|
f.called = append(f.called, id)
|
|
if err, ok := f.failOn[id]; ok {
|
|
return nil, err
|
|
}
|
|
return f.outcome, nil
|
|
}
|
|
|
|
func TestRun_DeletesEveryNonEmptyIDAndClears(t *testing.T) {
|
|
r := &fakeResetter{ids: []string{"a", "b", "", "c"}}
|
|
td := &fakeDeleter{}
|
|
res, err := Run(context.Background(), r, td, nil)
|
|
if err != nil {
|
|
t.Fatalf("Run: %v", err)
|
|
}
|
|
if res.TasksDeleted != 3 {
|
|
t.Fatalf("TasksDeleted=%d want 3", res.TasksDeleted)
|
|
}
|
|
if got := len(td.called); got != 3 {
|
|
t.Fatalf("DeleteTaskTree calls=%d want 3", got)
|
|
}
|
|
if !r.cleared {
|
|
t.Fatalf("Clear not called")
|
|
}
|
|
}
|
|
|
|
func TestRun_ContinuesOnDeleteFailure(t *testing.T) {
|
|
r := &fakeResetter{ids: []string{"ok1", "bad", "ok2"}}
|
|
td := &fakeDeleter{failOn: map[string]error{"bad": errors.New("boom")}}
|
|
res, err := Run(context.Background(), r, td, nil)
|
|
if err != nil {
|
|
t.Fatalf("Run: %v", err)
|
|
}
|
|
if res.TasksDeleted != 2 {
|
|
t.Fatalf("TasksDeleted=%d want 2 (skip failing)", res.TasksDeleted)
|
|
}
|
|
if !r.cleared {
|
|
t.Fatalf("Clear must still run even when a delete failed")
|
|
}
|
|
}
|
|
|
|
func TestRun_PropagatesListError(t *testing.T) {
|
|
r := &fakeResetter{listErr: errors.New("db down")}
|
|
td := &fakeDeleter{}
|
|
_, err := Run(context.Background(), r, td, nil)
|
|
if err == nil {
|
|
t.Fatalf("expected list error to surface")
|
|
}
|
|
}
|
|
|
|
func TestRun_PropagatesClearError(t *testing.T) {
|
|
r := &fakeResetter{ids: []string{"a"}, clearErr: errors.New("write fail")}
|
|
td := &fakeDeleter{}
|
|
res, err := Run(context.Background(), r, td, nil)
|
|
if err == nil {
|
|
t.Fatalf("expected clear error to surface")
|
|
}
|
|
if res.TasksDeleted != 1 {
|
|
t.Fatalf("partial TasksDeleted should still be reported, got %d", res.TasksDeleted)
|
|
}
|
|
}
|
|
|
|
func TestPreview_CountsOnlyNonEmptyIDs(t *testing.T) {
|
|
r := &fakeResetter{ids: []string{"", "a", "", "b", "c"}}
|
|
n, err := Preview(context.Background(), r)
|
|
if err != nil {
|
|
t.Fatalf("Preview: %v", err)
|
|
}
|
|
if n != 3 {
|
|
t.Fatalf("n=%d want 3", n)
|
|
}
|
|
}
|
|
|
|
func TestRun_RejectsNilArgs(t *testing.T) {
|
|
if _, err := Run(context.Background(), nil, &fakeDeleter{}, nil); err == nil {
|
|
t.Fatalf("nil Resetter must error")
|
|
}
|
|
if _, err := Run(context.Background(), &fakeResetter{}, nil, nil); err == nil {
|
|
t.Fatalf("nil TaskDeleter must error")
|
|
}
|
|
}
|
|
|
|
// TestRun_RejectsAlreadyCancelledContext pins the ctx.Err() pre-check:
|
|
// WithoutCancel intentionally drops every cancellation signal, so without
|
|
// this guard a caller passing an already-expired ctx (deadline missed
|
|
// before Run was even invoked) would silently get a destructive reset.
|
|
func TestRun_RejectsAlreadyCancelledContext(t *testing.T) {
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
cancel()
|
|
r := &fakeResetter{ids: []string{"a"}}
|
|
td := &fakeDeleter{}
|
|
_, err := Run(ctx, r, td, nil)
|
|
if err == nil {
|
|
t.Fatalf("expected error for already-cancelled context")
|
|
}
|
|
if r.cleared {
|
|
t.Fatalf("Clear must not run when context is already cancelled")
|
|
}
|
|
if len(td.called) != 0 {
|
|
t.Fatalf("DeleteTaskTree must not run when context is already cancelled, got %d calls", len(td.called))
|
|
}
|
|
}
|