172 lines
4.1 KiB
Go
172 lines
4.1 KiB
Go
package cli
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"errors"
|
|
"strings"
|
|
"testing"
|
|
|
|
"email-mcp/internal/secretstore"
|
|
)
|
|
|
|
var _ func() *App = NewApp
|
|
|
|
type promptStub struct {
|
|
credential secretstore.Credential
|
|
err error
|
|
called bool
|
|
}
|
|
|
|
func (p *promptStub) PromptSetup(context.Context) (secretstore.Credential, error) {
|
|
p.called = true
|
|
if p.err != nil {
|
|
return secretstore.Credential{}, p.err
|
|
}
|
|
return p.credential, nil
|
|
}
|
|
|
|
type storeStub struct {
|
|
saved secretstore.Credential
|
|
savedKey string
|
|
saveErr error
|
|
saveCalled bool
|
|
}
|
|
|
|
func (s *storeStub) Save(_ context.Context, key string, cred secretstore.Credential) error {
|
|
s.saveCalled = true
|
|
s.savedKey = key
|
|
s.saved = cred
|
|
return s.saveErr
|
|
}
|
|
|
|
func (s *storeStub) Load(context.Context, string) (secretstore.Credential, error) {
|
|
return secretstore.Credential{}, nil
|
|
}
|
|
|
|
type runnerStub struct {
|
|
err error
|
|
called bool
|
|
}
|
|
|
|
func (r *runnerStub) Run(context.Context) error {
|
|
r.called = true
|
|
return r.err
|
|
}
|
|
|
|
func TestAppRunRejectsUnknownCommand(t *testing.T) {
|
|
app := NewApp()
|
|
|
|
err := app.Run([]string{"unknown"})
|
|
if err == nil {
|
|
t.Fatal("expected error for unknown command")
|
|
}
|
|
}
|
|
|
|
func TestAppRunShowsUsageWhenNoArgsProvided(t *testing.T) {
|
|
app := NewApp()
|
|
|
|
err := app.Run(nil)
|
|
if err == nil {
|
|
t.Fatal("expected error for missing command")
|
|
}
|
|
if !strings.Contains(err.Error(), "usage:") {
|
|
t.Fatalf("expected usage text in error, got %q", err.Error())
|
|
}
|
|
}
|
|
|
|
func TestAppRunSetupPromptsAndSavesDefaultCredential(t *testing.T) {
|
|
store := &storeStub{}
|
|
prompter := &promptStub{
|
|
credential: secretstore.Credential{
|
|
Host: "imap.example.com",
|
|
Username: "alice",
|
|
Password: "secret",
|
|
},
|
|
}
|
|
app := NewAppWithDependencies(prompter, store, nil, &bytes.Buffer{})
|
|
|
|
if err := app.Run([]string{"setup"}); err != nil {
|
|
t.Fatalf("Run returned error: %v", err)
|
|
}
|
|
|
|
if !prompter.called {
|
|
t.Fatal("expected setup prompter to be called")
|
|
}
|
|
if !store.saveCalled {
|
|
t.Fatal("expected store Save to be called")
|
|
}
|
|
if store.savedKey != secretstore.DefaultAccountKey {
|
|
t.Fatalf("expected saved key %q, got %q", secretstore.DefaultAccountKey, store.savedKey)
|
|
}
|
|
if store.saved.Host != "imap.example.com" || store.saved.Username != "alice" || store.saved.Password != "secret" {
|
|
t.Fatalf("unexpected saved credential: %#v", store.saved)
|
|
}
|
|
}
|
|
|
|
func TestAppRunSetupRejectsInvalidCredential(t *testing.T) {
|
|
store := &storeStub{}
|
|
app := NewAppWithDependencies(&promptStub{
|
|
credential: secretstore.Credential{
|
|
Host: "imap.example.com",
|
|
Username: "alice",
|
|
},
|
|
}, store, nil, &bytes.Buffer{})
|
|
|
|
err := app.Run([]string{"setup"})
|
|
if err == nil {
|
|
t.Fatal("expected setup to fail for invalid credential")
|
|
}
|
|
if !strings.Contains(err.Error(), "password is required") {
|
|
t.Fatalf("expected validation error, got %v", err)
|
|
}
|
|
if store.saveCalled {
|
|
t.Fatal("expected store Save not to be called when credential is invalid")
|
|
}
|
|
}
|
|
|
|
func TestAppRunReturnsPromptError(t *testing.T) {
|
|
expectedErr := errors.New("prompt failed")
|
|
app := NewAppWithDependencies(&promptStub{err: expectedErr}, &storeStub{}, nil, &bytes.Buffer{})
|
|
|
|
err := app.Run([]string{"setup"})
|
|
if !errors.Is(err, expectedErr) {
|
|
t.Fatalf("expected prompt error %v, got %v", expectedErr, err)
|
|
}
|
|
}
|
|
|
|
func TestAppRunMCPDelegatesToRunner(t *testing.T) {
|
|
runner := &runnerStub{}
|
|
app := NewAppWithDependencies(nil, nil, runner, &bytes.Buffer{})
|
|
|
|
if err := app.Run([]string{"mcp"}); err != nil {
|
|
t.Fatalf("expected mcp command to succeed, got %v", err)
|
|
}
|
|
if !runner.called {
|
|
t.Fatal("expected MCP runner to be called")
|
|
}
|
|
}
|
|
|
|
func TestAppRunReturnsClearErrorsWhenDependenciesMissing(t *testing.T) {
|
|
app := NewApp()
|
|
|
|
tests := []struct {
|
|
command string
|
|
want string
|
|
}{
|
|
{command: "setup", want: "setup prompter is not configured"},
|
|
{command: "mcp", want: "mcp runner is not configured"},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.command, func(t *testing.T) {
|
|
err := app.Run([]string{tt.command})
|
|
if err == nil {
|
|
t.Fatalf("expected %s to fail without dependencies", tt.command)
|
|
}
|
|
if !strings.Contains(err.Error(), tt.want) {
|
|
t.Fatalf("expected error to contain %q, got %v", tt.want, err)
|
|
}
|
|
})
|
|
}
|
|
}
|