profiles/internal/rpc: more verror conversions and tests.

MultiPart: 2/2

Change-Id: Id0e77e55764b6df0116566ff5052b2056c418b40
diff --git a/profiles/internal/rpc/test/client_test.go b/profiles/internal/rpc/test/client_test.go
index 7374a2d..8b49b31 100644
--- a/profiles/internal/rpc/test/client_test.go
+++ b/profiles/internal/rpc/test/client_test.go
@@ -7,6 +7,7 @@
 import (
 	"fmt"
 	"io"
+	"net"
 	"os"
 	"path/filepath"
 	"runtime"
@@ -20,12 +21,15 @@
 	"v.io/v23/options"
 	"v.io/v23/rpc"
 	"v.io/v23/security"
+	"v.io/v23/vdlroot/signature"
 	"v.io/v23/verror"
 
 	"v.io/x/ref/envvar"
 	_ "v.io/x/ref/profiles"
 	inaming "v.io/x/ref/profiles/internal/naming"
 	irpc "v.io/x/ref/profiles/internal/rpc"
+	"v.io/x/ref/profiles/internal/rpc/stream/message"
+	"v.io/x/ref/profiles/internal/testing/mocks/mocknet"
 	"v.io/x/ref/services/mounttable/mounttablelib"
 	"v.io/x/ref/test"
 	"v.io/x/ref/test/expect"
@@ -36,11 +40,19 @@
 //go:generate v23 test generate .
 
 func rootMT(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
+	seclevel := options.SecurityConfidential
+	if len(args) == 1 && args[0] == "nosec" {
+		seclevel = options.SecurityNone
+	}
+	return runRootMT(stdin, stdout, stderr, seclevel, env, args...)
+}
+
+func runRootMT(stdin io.Reader, stdout, stderr io.Writer, seclevel options.SecurityLevel, env map[string]string, args ...string) error {
 	ctx, shutdown := v23.Init()
 	defer shutdown()
 
 	lspec := v23.GetListenSpec(ctx)
-	server, err := v23.NewServer(ctx, options.ServesMountTable(true))
+	server, err := v23.NewServer(ctx, options.ServesMountTable(true), seclevel)
 	if err != nil {
 		return fmt.Errorf("root failed: %v", err)
 	}
@@ -138,12 +150,12 @@
 	return ctx, shutdown
 }
 
-func runMountTable(t *testing.T, ctx *context.T) (*modules.Shell, func()) {
+func runMountTable(t *testing.T, ctx *context.T, args ...string) (*modules.Shell, func()) {
 	sh, err := modules.NewShell(ctx, nil, testing.Verbose(), t)
 	if err != nil {
 		t.Fatalf("unexpected error: %s", err)
 	}
-	root, err := sh.Start("rootMT", nil)
+	root, err := sh.Start("rootMT", nil, args...)
 	if err != nil {
 		t.Fatalf("unexpected error for root mt: %s", err)
 	}
@@ -250,14 +262,17 @@
 	}
 }
 
-func logErrors(t *testing.T, logerr, logstack bool, err error) {
+func logErrors(t *testing.T, msg string, logerr, logstack, debugString bool, err error) {
 	_, file, line, _ := runtime.Caller(2)
 	loc := fmt.Sprintf("%s:%d", filepath.Base(file), line)
 	if logerr {
-		t.Logf("%s: %v", loc, err)
+		t.Logf("%s: %s: %v", loc, msg, err)
 	}
 	if logstack {
-		t.Logf("%s: %v", loc, verror.Stack(err).String())
+		t.Logf("%s: %s: %v", loc, msg, verror.Stack(err).String())
+	}
+	if debugString {
+		t.Logf("%s: %s: %v", loc, msg, verror.DebugString(err))
 	}
 }
 
@@ -269,52 +284,51 @@
 	ns := v23.GetNamespace(ctx)
 	v23.GetNamespace(ctx).CacheCtl(naming.DisableCache(true))
 
-	logErr := func(err error) {
-		logErrors(t, true, false, err)
+	logErr := func(msg string, err error) {
+		logErrors(t, msg, true, false, false, err)
 	}
 
 	emptyCtx := &context.T{}
 	_, err := client.StartCall(emptyCtx, "noname", "nomethod", nil)
-	logErr(err)
 	if verror.ErrorID(err) != verror.ErrBadArg.ID {
 		t.Fatalf("wrong error: %s", err)
 	}
+	logErr("no context", err)
 
 	p1 := options.ServerPublicKey{testutil.NewPrincipal().PublicKey()}
 	p2 := options.ServerPublicKey{testutil.NewPrincipal().PublicKey()}
 	_, err = client.StartCall(ctx, "noname", "nomethod", nil, p1, p2)
-	logErr(err)
 	if verror.ErrorID(err) != verror.ErrBadArg.ID {
 		t.Fatalf("wrong error: %s", err)
 	}
+	logErr("too many public keys", err)
 
 	// This will fail with NoServers, but because there is no mount table
 	// to communicate with. The error message should include a
 	// 'connection refused' string.
 	ns.SetRoots("/127.0.0.1:8101")
 	_, err = client.StartCall(ctx, "noname", "nomethod", nil, options.NoRetry{})
-	logErr(err)
 	if verror.ErrorID(err) != verror.ErrNoServers.ID {
 		t.Fatalf("wrong error: %s", err)
 	}
-	if want := "connection refused"; !strings.Contains(err.Error(), want) {
+	if want := "connection refused"; !strings.Contains(verror.DebugString(err), want) {
 		t.Fatalf("wrong error: %s - doesn't contain %q", err, want)
 	}
+	logErr("no mount table", err)
 
 	// This will fail with NoServers, but because there really is no
 	// name registered with the mount table.
 	_, shutdown = runMountTable(t, ctx)
 	defer shutdown()
 	_, err = client.StartCall(ctx, "noname", "nomethod", nil, options.NoRetry{})
-	logErr(err)
 	if verror.ErrorID(err) != verror.ErrNoServers.ID {
 		t.Fatalf("wrong error: %s", err)
 	}
 	roots := ns.Roots()
-
 	if unwanted := "connection refused"; strings.Contains(err.Error(), unwanted) {
 		t.Fatalf("wrong error: %s - does contain %q", err, unwanted)
 	}
+	logErr("no name registered", err)
 
 	// The following tests will fail with NoServers, but because there are
 	// no protocols that the client and servers (mount table, and "name") share.
@@ -328,20 +342,18 @@
 	// This will fail in its attempt to call ResolveStep to the mount table
 	// because we are using both the new context and the new client.
 	_, err = nclient.StartCall(nctx, "name", "nomethod", nil, options.NoRetry{})
-	logErr(err)
 	if verror.ErrorID(err) != verror.ErrNoServers.ID {
 		t.Fatalf("wrong error: %s", err)
 	}
 	if want := "ResolveStep"; !strings.Contains(err.Error(), want) {
 		t.Fatalf("wrong error: %s - doesn't contain %q", err, want)
 	}
+	logErr("mismatched protocols", err)
 
 	// This will fail in its attempt to invoke the actual RPC because
 	// we are using the old context (which supplies the context for the calls
 	// to ResolveStep) and the new client.
 	_, err = nclient.StartCall(ctx, "name", "nomethod", nil, options.NoRetry{})
-	logErr(err)
-
 	if verror.ErrorID(err) != verror.ErrNoServers.ID {
 		t.Fatalf("wrong error: %s", err)
 	}
@@ -352,6 +364,7 @@
 		t.Fatalf("wrong error: %s - does contain %q", err, unwanted)
 
 	}
+	logErr("mismatched protocols", err)
 
 	// The following two tests will fail due to a timeout.
 	ns.SetRoots("/203.0.113.10:8101")
@@ -368,7 +381,7 @@
 	if call != nil {
 		t.Fatalf("expected call to be nil")
 	}
-	logErr(err)
+	logErr("timeout to mount table", err)
 
 	// This, second test, will fail due a timeout contacting the server itself.
 	ns.SetRoots(roots...)
@@ -385,7 +398,115 @@
 	if call != nil {
 		t.Fatalf("expected call to be nil")
 	}
-	logErr(err)
+	logErr("timeout to server", err)
+}
+
+func dropDataDialer(network, address string, timeout time.Duration) (net.Conn, error) {
+	matcher := func(read bool, msg message.T) bool {
+		switch msg.(type) {
+		case *message.Data:
+			return true
+		}
+		return false
+	}
+	opts := mocknet.Opts{
+		Mode:              mocknet.V23CloseAtMessage,
+		V23MessageMatcher: matcher,
+	}
+	return mocknet.DialerWithOpts(opts, network, address, timeout)
+}
+
+func TestStartCallBadProtocol(t *testing.T) {
+	ctx, shutdown := newCtx()
+	defer shutdown()
+	client := v23.GetClient(ctx)
+
+	ns := v23.GetNamespace(ctx)
+	ns.CacheCtl(naming.DisableCache(true))
+
+	logErr := func(msg string, err error) {
+		logErrors(t, msg, true, false, false, err)
+	}
+
+	rpc.RegisterProtocol("dropData", dropDataDialer, net.Listen)
+
+	// The following test will fail due to a broken connection.
+	// We need to run mount table and servers with no security to use
+	// the V23CloseAtMessage net.Conn mock.
+	_, shutdown = runMountTable(t, ctx, "nosec")
+	defer shutdown()
+
+	roots := ns.Roots()
+	brkRoot, err := mocknet.RewriteEndpointProtocol(roots[0], "dropData")
+	if err != nil {
+		t.Fatal(err)
+	}
+	ns.SetRoots(brkRoot.Name())
+
+	nctx, _ := context.WithTimeout(ctx, 100*time.Millisecond)
+	call, err := client.StartCall(nctx, "name", "noname", nil, options.NoRetry{}, options.SecurityNone)
+	if verror.ErrorID(err) != verror.ErrBadProtocol.ID {
+		t.Fatalf("wrong error: %s", err)
+	}
+	if call != nil {
+		t.Fatalf("expected call to be nil")
+	}
+	logErr("broken connection", err)
+
+	// The following test will fail with because the client will set up
+	// a secure connection to a server that isn't expecting one.
+	name, fn := initServer(t, ctx, options.SecurityNone)
+	defer fn()
+
+	call, err = client.StartCall(nctx, name, "noname", nil, options.NoRetry{})
+	if verror.ErrorID(err) != verror.ErrNoServers.ID {
+		t.Fatalf("wrong error: %s", err)
+	}
+	if call != nil {
+		t.Fatalf("expected call to be nil")
+	}
+	logErr("insecure server", err)
+
+	// This is the inverse, secure server, insecure client
+	name, fn = initServer(t, ctx)
+	defer fn()
+
+	call, err = client.StartCall(nctx, name, "noname", nil, options.NoRetry{}, options.SecurityNone)
+	if verror.ErrorID(err) != verror.ErrBadProtocol.ID {
+		t.Fatalf("wrong error: %s", err)
+	}
+	if call != nil {
+		t.Fatalf("expected call to be nil")
+	}
+	logErr("insecure client", err)
+}
+
+func TestStartCallSecurity(t *testing.T) {
+	ctx, shutdown := newCtx()
+	defer shutdown()
+	client := v23.GetClient(ctx)
+
+	logErr := func(msg string, err error) {
+		logErrors(t, msg, true, false, false, err)
+	}
+
+	name, fn := initServer(t, ctx)
+	defer fn()
+
+	// Create a context with a new principal that doesn't match the server,
+	// so that the client will not trust the server.
+	ctx1, err := v23.SetPrincipal(ctx, testutil.NewPrincipal("test-blessing"))
+	if err != nil {
+		t.Fatal(err)
+	}
+	call, err := client.StartCall(ctx1, name, "noname", nil, options.NoRetry{})
+	if verror.ErrorID(err) != verror.ErrNotTrusted.ID {
+		t.Fatalf("wrong error: %s", err)
+	}
+	if call != nil {
+		t.Fatalf("expected call to be nil")
+	}
+	logErr("client does not trust server", err)
 }
 
 func childPing(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
@@ -402,8 +523,8 @@
 	return nil
 }
 
-func initServer(t *testing.T, ctx *context.T) (string, func()) {
-	server, err := v23.NewServer(ctx)
+func initServer(t *testing.T, ctx *context.T, opts ...rpc.ServerOpt) (string, func()) {
+	server, err := v23.NewServer(ctx, opts...)
 	if err != nil {
 		t.Fatalf("unexpected error: %s", err)
 	}
@@ -652,8 +773,7 @@
 	_, fn := runMountTable(t, ctx)
 	defer fn()
 	name := "noservers"
-	ctx, _ = context.WithTimeout(ctx, 1000*time.Millisecond)
-	call, err := v23.GetClient(ctx).StartCall(ctx, name, "Sleep", nil)
+	call, err := v23.GetClient(ctx).StartCall(ctx, name, "Sleep", nil, options.NoRetry{})
 	if err != nil {
 		testForVerror(t, err, verror.ErrNoServers)
 		return
@@ -736,5 +856,138 @@
 	}
 }
 
-// TODO(cnicolaou:) tests for:
-// -- Test for bad discharges error and correct invalidation, client.go:870..880
+func TestMethodErrors(t *testing.T) {
+	ctx, shutdown := newCtx()
+	defer shutdown()
+	clt := v23.GetClient(ctx)
+
+	name, fn := initServer(t, ctx)
+	defer fn()
+
+	logErr := func(msg string, err error) {
+		logErrors(t, msg, true, false, false, err)
+	}
+
+	// Unknown method
+	call, err := clt.StartCall(ctx, name, "NoMethod", nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+	verr := call.Finish()
+	if verror.ErrorID(verr) != verror.ErrUnknownMethod.ID {
+		t.Fatalf("wrong error: %s", verr)
+	}
+	logErr("unknown method", verr)
+
+	// Unknown suffix
+	call, err = clt.StartCall(ctx, name+"/NoSuffix", "Ping", nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+	verr = call.Finish()
+	if verror.ErrorID(verr) != verror.ErrUnknownSuffix.ID {
+		t.Fatalf("wrong error: %s", verr)
+	}
+	logErr("unknown suffix", verr)
+
+	// Too many args.
+	call, err = clt.StartCall(ctx, name, "Ping", []interface{}{1, 2})
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	r1 := ""
+	verr = call.Finish(&r1)
+	if verror.ErrorID(verr) != verror.ErrBadProtocol.ID {
+		t.Fatalf("wrong error: %s", verr)
+	}
+	if got, want := verr.Error(), "wrong number of input arguments"; !strings.Contains(got, want) {
+		t.Fatalf("want %q to contain %q", got, want)
+	}
+	logErr("wrong # args", verr)
+
+	// Too many results.
+	call, err = clt.StartCall(ctx, name, "Ping", nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	r2 := ""
+	verr = call.Finish(&r1, &r2)
+	if verror.ErrorID(verr) != verror.ErrBadProtocol.ID {
+		t.Fatalf("wrong error: %s", verr)
+	}
+	if got, want := verr.Error(), "results, but want"; !strings.Contains(got, want) {
+		t.Fatalf("want %q to contain %q", got, want)
+	}
+	logErr("wrong # results", verr)
+
+	// Mismatched arg types
+	call, err = clt.StartCall(ctx, name, "Echo", []interface{}{1})
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	verr = call.Finish(&r2)
+	if verror.ErrorID(verr) != verror.ErrBadProtocol.ID {
+		t.Fatalf("wrong error: %s", verr)
+	}
+	if got, want := verr.Error(), "aren't compatible"; !strings.Contains(got, want) {
+		t.Fatalf("want %q to contain %q", got, want)
+	}
+	logErr("wrong arg types", verr)
+
+	// Mismatched result types
+	call, err = clt.StartCall(ctx, name, "Ping", nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	r3 := 2
+	verr = call.Finish(&r3)
+	if verror.ErrorID(verr) != verror.ErrBadProtocol.ID {
+		t.Fatalf("wrong error: %s", verr)
+	}
+	if got, want := verr.Error(), "aren't compatible"; !strings.Contains(got, want) {
+		t.Fatalf("want %q to contain %q", got, want)
+	}
+	logErr("wrong result types", verr)
+}
+
+func TestReservedMethodErrors(t *testing.T) {
+	ctx, shutdown := newCtx()
+	defer shutdown()
+	clt := v23.GetClient(ctx)
+
+	name, fn := initServer(t, ctx)
+	defer fn()
+
+	logErr := func(msg string, err error) {
+		logErrors(t, msg, true, false, false, err)
+	}
+
+	// This call will fail because the __xx suffix is not supported by
+	// the dispatcher implementing Signature.
+	call, err := clt.StartCall(ctx, name+"/__xx", "__Signature", nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+	sig := []signature.Interface{}
+	verr := call.Finish(&sig)
+	if verror.ErrorID(verr) != verror.ErrUnknownSuffix.ID {
+		t.Fatalf("wrong error: %s", verr)
+	}
+	logErr("unknown suffix", verr)
+
+	// This call will fail for the same reason, but with a different error,
+	// saying that MethodSignature is an unknown method.
+	call, err = clt.StartCall(ctx, name+"/__xx", "__MethodSignature", []interface{}{"dummy"})
+	if err != nil {
+		t.Fatal(err)
+	}
+	verr = call.Finish(&sig)
+	if verror.ErrorID(verr) != verror.ErrUnknownMethod.ID {
+		t.Fatalf("wrong error: %s", verr)
+	}
+	logErr("unknown method", verr)
+}
diff --git a/profiles/internal/rpc/test/proxy_test.go b/profiles/internal/rpc/test/proxy_test.go
index bf6ede5..ed47050 100644
--- a/profiles/internal/rpc/test/proxy_test.go
+++ b/profiles/internal/rpc/test/proxy_test.go
@@ -80,9 +80,7 @@
 			if expected == len(pubState) {
 				break
 			}
-			fmt.Fprintf(stderr, "%s\n", pub.DebugString())
 			delay := time.Second
-			fmt.Fprintf(stderr, "Sleeping: %s\n", delay)
 			time.Sleep(delay)
 		}
 	}
diff --git a/profiles/internal/rpc/test/simple_test.go b/profiles/internal/rpc/test/simple_test.go
index bc653f4..c83ec25 100644
--- a/profiles/internal/rpc/test/simple_test.go
+++ b/profiles/internal/rpc/test/simple_test.go
@@ -29,6 +29,10 @@
 	return "pong", nil
 }
 
+func (s *simple) Echo(call rpc.ServerCall, arg string) (string, error) {
+	return arg, nil
+}
+
 func (s *simple) Source(call rpc.StreamServerCall, start int) error {
 	i := start
 	backoff := 25 * time.Millisecond