veyron/tools/mgmt/nodex: completed adding remaining tests for nodex command

This CL completes test coverage for the nodex command and includes
some code cleanup of the mock nodemanager used in the tests.

Change-Id: I52e372b3e440d10f132e9a26c618a424c72b365a
diff --git a/tools/mgmt/nodex/impl_test.go b/tools/mgmt/nodex/impl_test.go
index 462f9f5..b154e43 100644
--- a/tools/mgmt/nodex/impl_test.go
+++ b/tools/mgmt/nodex/impl_test.go
@@ -10,6 +10,7 @@
 	"veyron.io/veyron/veyron2/naming"
 	"veyron.io/veyron/veyron2/rt"
 	"veyron.io/veyron/veyron2/services/mgmt/node"
+	"veyron.io/veyron/veyron2/verror"
 )
 
 func TestListCommand(t *testing.T) {
@@ -79,6 +80,7 @@
 	var stdout, stderr bytes.Buffer
 	cmd.Init(nil, &stdout, &stderr)
 	nodeName := naming.JoinAddressName(endpoint.String(), "/myapp/1")
+	//nodeName := endpoint.String()
 
 	if err := cmd.Execute([]string{"add", "one"}); err == nil {
 		t.Fatalf("wrongly failed to receive a non-nil error.")
@@ -129,7 +131,7 @@
 	cmd := root()
 	var stdout, stderr bytes.Buffer
 	cmd.Init(nil, &stdout, &stderr)
-	nodeName := naming.JoinAddressName(endpoint.String(), "/myapp/1")
+	nodeName := naming.JoinAddressName(endpoint.String(), "")
 
 	if err := cmd.Execute([]string{"remove", "one"}); err == nil {
 		t.Fatalf("wrongly failed to receive a non-nil error.")
@@ -167,7 +169,7 @@
 	cmd := root()
 	var stdout, stderr bytes.Buffer
 	cmd.Init(nil, &stdout, &stderr)
-	nodeName := naming.JoinAddressName(endpoint.String(), "/myapp/1")
+	nodeName := naming.JoinAddressName(endpoint.String(), "")
 
 	if err := cmd.Execute([]string{"install", "blech"}); err == nil {
 		t.Fatalf("wrongly failed to receive a non-nil error.")
@@ -210,3 +212,163 @@
 	tape.Rewind()
 	stdout.Reset()
 }
+
+func TestClaimCommand(t *testing.T) {
+	runtime := rt.Init()
+	tape := NewTape()
+	server, endpoint, err := startServer(t, runtime, tape)
+	if err != nil {
+		return
+	}
+	defer stopServer(t, server)
+
+	// Setup the command-line.
+	cmd := root()
+	var stdout, stderr bytes.Buffer
+	cmd.Init(nil, &stdout, &stderr)
+	nodeName := naming.JoinAddressName(endpoint.String(), "")
+	
+
+	// Confirm that we correctly enforce the number of arguments.
+	if err := cmd.Execute([]string{"claim", "nope"}); err == nil {
+		t.Fatalf("wrongly failed to receive a non-nil error.")
+	}
+	if expected, got := "ERROR: claim: incorrect number of arguments, expected 2, got 1", strings.TrimSpace(stderr.String()); !strings.HasPrefix(got, expected) {
+		t.Fatalf("Unexpected output from claim. Got %q, expected prefix %q", got, expected)
+	}
+	stdout.Reset()
+	stderr.Reset()
+	tape.Rewind()
+
+	if err := cmd.Execute([]string{"claim", "nope", "nope", "nope"}); err == nil {
+		t.Fatalf("wrongly failed to receive a non-nil error.")
+	}
+	if expected, got := "ERROR: claim: incorrect number of arguments, expected 2, got 3", strings.TrimSpace(stderr.String()); !strings.HasPrefix(got, expected) {
+		t.Fatalf("Unexpected output from claim. Got %q, expected prefix %q", got, expected)
+	}
+	stdout.Reset()
+	stderr.Reset()
+	tape.Rewind()
+
+	// Correct operation.
+	tape.SetResponses([]interface{}{
+		nil,
+	})
+	if err := cmd.Execute([]string{"claim", nodeName, "grant"}); err != nil {
+		t.Fatalf("Claim(%s, %s) failed: %v", nodeName, "grant", err)
+	}
+	if got, expected := len(tape.Play()), 1; got != expected {
+		t.Errorf("invalid call sequence. Got %v, want %v", got, expected)
+	}
+	if expected, got := "Successfully claimed.", strings.TrimSpace(stdout.String()); !strings.HasPrefix(got, expected) {
+		t.Fatalf("Unexpected output from claim. Got %q, expected prefix %q", got, expected)
+	}
+	expected := []interface{}{
+		"Claim",
+	}
+	if got := tape.Play(); !reflect.DeepEqual(expected, got) {
+		t.Errorf("unexpected result. Got %v want %v", got, expected)
+	}
+	tape.Rewind()
+	stdout.Reset()
+	stderr.Reset()
+
+	// Error operation.
+	tape.SetResponses([]interface{}{
+		verror.BadArgf("oops!"),
+	})
+	if err := cmd.Execute([]string{"claim", nodeName, "grant"}); err == nil {
+		t.Fatalf("claim() failed to detect error", err)
+	}
+	expected = []interface{}{
+		"Claim",
+	}
+	if got := tape.Play(); !reflect.DeepEqual(expected, got) {
+		t.Errorf("unexpected result. Got %v want %v", got, expected)
+	}
+	tape.Rewind()
+	stdout.Reset()
+	stderr.Reset()
+
+}
+
+func TestStartCommand(t *testing.T) {
+	runtime := rt.Init()
+	tape := NewTape()
+	server, endpoint, err := startServer(t, runtime, tape)
+	if err != nil {
+		return
+	}
+	defer stopServer(t, server)
+
+	// Setup the command-line.
+	cmd := root()
+	var stdout, stderr bytes.Buffer
+	cmd.Init(nil, &stdout, &stderr)
+	appName := naming.JoinAddressName(endpoint.String(), "")
+
+	// Confirm that we correctly enforce the number of arguments.
+	if err := cmd.Execute([]string{"start", "nope"}); err == nil {
+		t.Fatalf("wrongly failed to receive a non-nil error.")
+	}
+	if expected, got := "ERROR: start: incorrect number of arguments, expected 2, got 1", strings.TrimSpace(stderr.String()); !strings.HasPrefix(got, expected) {
+		t.Fatalf("Unexpected output from start. Got %q, expected prefix %q", got, expected)
+	}
+	stdout.Reset()
+	stderr.Reset()
+	tape.Rewind()
+
+	if err := cmd.Execute([]string{"start", "nope", "nope", "nope"}); err == nil {
+		t.Fatalf("wrongly failed to receive a non-nil error.")
+	}
+	if expected, got := "ERROR: start: incorrect number of arguments, expected 2, got 3", strings.TrimSpace(stderr.String()); !strings.HasPrefix(got, expected) {
+		t.Fatalf("Unexpected output from start. Got %q, expected prefix %q", got, expected)
+	}
+	stdout.Reset()
+	stderr.Reset()
+	tape.Rewind()
+
+	// Correct operation.
+	tape.SetResponses([]interface{}{StartResponse{
+		appIds: []string{"app1", "app2"},
+		err:    nil,
+	},
+	})
+	if err := cmd.Execute([]string{"start", appName, "grant"}); err != nil {
+		t.Fatalf("Start(%s, %s) failed: %v", appName, "grant", err)
+	}
+
+	b := new(bytes.Buffer)
+	fmt.Fprintf(b, "Successfully started: %q\nSuccessfully started: %q", appName+"/app1", appName+"/app2")
+	if expected, got := b.String(), strings.TrimSpace(stdout.String()); !strings.HasPrefix(got, expected) {
+		t.Fatalf("Unexpected output from start. Got %q, expected prefix %q", got, expected)
+	}
+	expected := []interface{}{
+		"Start",
+	}
+	if got := tape.Play(); !reflect.DeepEqual(expected, got) {
+		t.Errorf("unexpected result. Got %v want %v", got, expected)
+	}
+	tape.Rewind()
+	stdout.Reset()
+	stderr.Reset()
+
+	// Error operation.
+	tape.SetResponses([]interface{}{StartResponse{
+		[]string{},
+		verror.BadArgf("oops!"),
+	},
+	})
+	if err := cmd.Execute([]string{"start", appName, "grant"}); err == nil {
+		t.Fatalf("start failed to detect error")
+	}
+	expected = []interface{}{
+		"Start",
+	}
+	if got := tape.Play(); !reflect.DeepEqual(expected, got) {
+		t.Errorf("unexpected result. Got %v want %v", got, expected)
+	}
+	tape.Rewind()
+	stdout.Reset()
+	stderr.Reset()
+}
diff --git a/tools/mgmt/nodex/instance_impl_test.go b/tools/mgmt/nodex/instance_impl_test.go
new file mode 100644
index 0000000..be880ea
--- /dev/null
+++ b/tools/mgmt/nodex/instance_impl_test.go
@@ -0,0 +1,162 @@
+package main
+
+import (
+	"bytes"
+	"reflect"
+	"strings"
+	"testing"
+
+	"veyron.io/veyron/veyron2/naming"
+	"veyron.io/veyron/veyron2/rt"
+	"veyron.io/veyron/veyron2/verror"
+)
+
+func TestStopCommand(t *testing.T) {
+	runtime := rt.Init()
+	tape := NewTape()
+	server, endpoint, err := startServer(t, runtime, tape)
+	if err != nil {
+		return
+	}
+	defer stopServer(t, server)
+
+	// Setup the command-line.
+	cmd := root()
+	var stdout, stderr bytes.Buffer
+	cmd.Init(nil, &stdout, &stderr)
+	appName := naming.JoinAddressName(endpoint.String(), "")
+
+	// Confirm that we correctly enforce the number of arguments.
+	if err := cmd.Execute([]string{"stop"}); err == nil {
+		t.Fatalf("wrongly failed to receive a non-nil error.")
+	}
+	if expected, got := "ERROR: stop: incorrect number of arguments, expected 1, got 0", strings.TrimSpace(stderr.String()); !strings.HasPrefix(got, expected) {
+		t.Fatalf("Unexpected output from stop. Got %q, expected prefix %q", got, expected)
+	}
+	stdout.Reset()
+	stderr.Reset()
+	tape.Rewind()
+
+	if err := cmd.Execute([]string{"stop", "nope", "nope"}); err == nil {
+		t.Fatalf("wrongly failed to receive a non-nil error.")
+	}
+	if expected, got := "ERROR: stop: incorrect number of arguments, expected 1, got 2", strings.TrimSpace(stderr.String()); !strings.HasPrefix(got, expected) {
+		t.Fatalf("Unexpected output from stop. Got %q, expected prefix %q", got, expected)
+	}
+	stdout.Reset()
+	stderr.Reset()
+	tape.Rewind()
+
+	// Test the 'stop' command.
+	tape.SetResponses([]interface{}{
+		nil,
+	})
+
+	if err := cmd.Execute([]string{"stop", appName + "/appname"}); err != nil {
+		t.Fatalf("stop failed when it shouldn't: %v", err)
+	}
+	if expected, got := "Stop succeeded", strings.TrimSpace(stdout.String()); got != expected {
+		t.Fatalf("Unexpected output from list. Got %q, expected %q", got, expected)
+	}
+	expected := []interface{}{
+		StopStimulus{"Stop", 5},
+	}
+	if got := tape.Play(); !reflect.DeepEqual(expected, got) {
+		t.Errorf("invalid call sequence. Got %v, want %v", got, expected)
+	}
+	tape.Rewind()
+	stderr.Reset()
+	stdout.Reset()
+
+	// Test stop with bad parameters.
+	tape.SetResponses([]interface{}{
+		verror.BadArgf("oops!"),
+	})
+	if err := cmd.Execute([]string{"stop", appName + "/appname"}); err == nil {
+		t.Fatalf("wrongly didn't receive a non-nil error.")
+	}
+	// expected the same.
+	if got := tape.Play(); !reflect.DeepEqual(expected, got) {
+		t.Errorf("invalid call sequence. Got %v, want %v", got, expected)
+	}
+	tape.Rewind()
+	stderr.Reset()
+	stdout.Reset()
+}
+
+func testHelper(t *testing.T, lower, upper string) {
+	runtime := rt.Init()
+	tape := NewTape()
+	server, endpoint, err := startServer(t, runtime, tape)
+	if err != nil {
+		return
+	}
+	defer stopServer(t, server)
+
+	// Setup the command-line.
+	cmd := root()
+	var stdout, stderr bytes.Buffer
+	cmd.Init(nil, &stdout, &stderr)
+	appName := naming.JoinAddressName(endpoint.String(), "")
+
+	// Confirm that we correctly enforce the number of arguments.
+	if err := cmd.Execute([]string{lower}); err == nil {
+		t.Fatalf("wrongly failed to receive a non-nil error.")
+	}
+	if expected, got := "ERROR: "+lower+": incorrect number of arguments, expected 1, got 0", strings.TrimSpace(stderr.String()); !strings.HasPrefix(got, expected) {
+		t.Fatalf("Unexpected output from %s. Got %q, expected prefix %q", lower, got, expected)
+	}
+	stdout.Reset()
+	stderr.Reset()
+	tape.Rewind()
+
+	if err := cmd.Execute([]string{lower, "nope", "nope"}); err == nil {
+		t.Fatalf("wrongly failed to receive a non-nil error.")
+	}
+	if expected, got := "ERROR: "+lower+": incorrect number of arguments, expected 1, got 2", strings.TrimSpace(stderr.String()); !strings.HasPrefix(got, expected) {
+		t.Fatalf("Unexpected output from %s. Got %q, expected prefix %q", lower, got, expected)
+	}
+	stdout.Reset()
+	stderr.Reset()
+	tape.Rewind()
+
+	// Correct operation.
+	tape.SetResponses([]interface{}{
+		nil,
+	})
+	if err := cmd.Execute([]string{lower, appName + "/appname"}); err != nil {
+		t.Fatalf("%s failed when it shouldn't: %v", lower, err)
+	}
+	if expected, got := upper+" succeeded", strings.TrimSpace(stdout.String()); got != expected {
+		t.Fatalf("Unexpected output from %s. Got %q, expected %q", lower, got, expected)
+	}
+	if expected, got := []interface{}{upper}, tape.Play(); !reflect.DeepEqual(expected, got) {
+		t.Errorf("invalid call sequence. Got %v, want %v", got, expected)
+	}
+	tape.Rewind()
+	stderr.Reset()
+	stdout.Reset()
+
+	// Test list with bad parameters.
+	tape.SetResponses([]interface{}{
+		verror.BadArgf("oops!"),
+	})
+	if err := cmd.Execute([]string{lower, appName + "/appname"}); err == nil {
+		t.Fatalf("wrongly didn't receive a non-nil error.")
+	}
+	// expected the same.
+	if expected, got := []interface{}{upper}, tape.Play(); !reflect.DeepEqual(expected, got) {
+		t.Errorf("invalid call sequence. Got %v, want %v", got, expected)
+	}
+	tape.Rewind()
+	stderr.Reset()
+	stdout.Reset()
+}
+
+func TestSuspendCommand(t *testing.T) {
+	testHelper(t, "suspend", "Suspend")
+}
+
+func TestResumeCommand(t *testing.T) {
+	testHelper(t, "resume", "Resume")
+}
diff --git a/tools/mgmt/nodex/nodemanager_mock_test.go b/tools/mgmt/nodex/nodemanager_mock_test.go
index 6449110..83141da 100644
--- a/tools/mgmt/nodex/nodemanager_mock_test.go
+++ b/tools/mgmt/nodex/nodemanager_mock_test.go
@@ -42,19 +42,28 @@
 	accountName   string
 }
 
-func (i *mockNodeInvoker) AssociateAccount(call ipc.ServerContext, identityNames []string, accountName string) error {
-	ri := i.tape.Record(AddAssociationStimulus{"AssociateAccount", identityNames, accountName})
+// simpleCore implements the core of all mock methods that take
+// arguments and return error.
+func (mni *mockNodeInvoker) simpleCore(callRecord interface{}, name string) error {
+	ri := mni.tape.Record(callRecord)
 	switch r := ri.(type) {
 	case nil:
 		return nil
 	case error:
 		return r
 	}
-	log.Fatalf("AssociateAccount (mock) response %v is of bad type", ri)
+	log.Fatalf("%s (mock) response %v is of bad type", name, ri)
 	return nil
 }
 
-func (i *mockNodeInvoker) Claim(call ipc.ServerContext) error { return nil }
+func (mni *mockNodeInvoker) AssociateAccount(call ipc.ServerContext, identityNames []string, accountName string) error {
+	return mni.simpleCore(AddAssociationStimulus{"AssociateAccount", identityNames, accountName}, "AssociateAccount")
+}
+
+func (mni *mockNodeInvoker) Claim(call ipc.ServerContext) error {
+	return mni.simpleCore("Claim", "Claim")
+}
+
 func (*mockNodeInvoker) Describe(ipc.ServerContext) (node.Description, error) {
 	return node.Description{}, nil
 }
@@ -80,16 +89,40 @@
 	return r.appId, r.err
 }
 
-func (*mockNodeInvoker) Refresh(ipc.ServerContext) error           { return nil }
-func (*mockNodeInvoker) Restart(ipc.ServerContext) error           { return nil }
-func (*mockNodeInvoker) Resume(ipc.ServerContext) error            { return nil }
-func (i *mockNodeInvoker) Revert(call ipc.ServerContext) error     { return nil }
-func (*mockNodeInvoker) Start(ipc.ServerContext) ([]string, error) { return []string{}, nil }
-func (*mockNodeInvoker) Stop(ipc.ServerContext, uint32) error      { return nil }
-func (*mockNodeInvoker) Suspend(ipc.ServerContext) error           { return nil }
-func (*mockNodeInvoker) Uninstall(ipc.ServerContext) error         { return nil }
-func (i *mockNodeInvoker) Update(ipc.ServerContext) error          { return nil }
-func (*mockNodeInvoker) UpdateTo(ipc.ServerContext, string) error  { return nil }
+func (*mockNodeInvoker) Refresh(ipc.ServerContext) error { return nil }
+func (*mockNodeInvoker) Restart(ipc.ServerContext) error { return nil }
+
+func (mni *mockNodeInvoker) Resume(_ ipc.ServerContext) error {
+	return mni.simpleCore("Resume", "Resume")
+}
+func (i *mockNodeInvoker) Revert(call ipc.ServerContext) error { return nil }
+
+type StartResponse struct {
+	appIds []string
+	err    error
+}
+
+func (mni *mockNodeInvoker) Start(ipc.ServerContext) ([]string, error) {
+	ir := mni.tape.Record("Start")
+	r := ir.(StartResponse)
+	return r.appIds, r.err
+}
+
+type StopStimulus struct {
+	fun       string
+	timeDelta uint32
+}
+
+func (mni *mockNodeInvoker) Stop(_ ipc.ServerContext, timeDelta uint32) error {
+	return mni.simpleCore(StopStimulus{"Stop", timeDelta}, "Stop")
+}
+
+func (mni *mockNodeInvoker) Suspend(_ ipc.ServerContext) error {
+	return mni.simpleCore("Suspend", "Suspend")
+}
+func (*mockNodeInvoker) Uninstall(ipc.ServerContext) error        { return nil }
+func (i *mockNodeInvoker) Update(ipc.ServerContext) error         { return nil }
+func (*mockNodeInvoker) UpdateTo(ipc.ServerContext, string) error { return nil }
 
 // Mock ACL getting and setting
 type GetACLResponse struct {
@@ -105,15 +138,7 @@
 }
 
 func (mni *mockNodeInvoker) SetACL(_ ipc.ServerContext, acl access.TaggedACLMap, etag string) error {
-	ri := mni.tape.Record(SetACLStimulus{"SetACL", acl, etag})
-	switch r := ri.(type) {
-	case nil:
-		return nil
-	case error:
-		return r
-	}
-	log.Fatalf("AssociateAccount (mock) response %v is of bad type\n", ri)
-	return nil
+	return mni.simpleCore(SetACLStimulus{"SetACL", acl, etag}, "SetACL")
 }
 
 func (mni *mockNodeInvoker) GetACL(ipc.ServerContext) (access.TaggedACLMap, string, error) {