github.com/qri-io/qri@v0.10.1-0.20220104210721-c771715036cb/cmd/setup_test.go (about)

     1  package cmd
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"os"
     8  	"path/filepath"
     9  	"testing"
    10  
    11  	"github.com/ghodss/yaml"
    12  	"github.com/google/go-cmp/cmp"
    13  	"github.com/qri-io/ioes"
    14  	"github.com/qri-io/qri/auth/key"
    15  	testkeys "github.com/qri-io/qri/auth/key/test"
    16  	repotest "github.com/qri-io/qri/repo/test"
    17  	"github.com/spf13/cobra"
    18  )
    19  
    20  // Test that setup command object can be created
    21  func TestSetupComplete(t *testing.T) {
    22  	run := NewTestRunner(t, "test_peer_setup_complete", "qri_test_setup_complete")
    23  	defer run.Delete()
    24  
    25  	ctx, cancel := context.WithCancel(context.Background())
    26  	defer cancel()
    27  
    28  	f, err := NewTestFactory(ctx)
    29  	if err != nil {
    30  		t.Errorf("error creating new test factory: %s", err)
    31  		return
    32  	}
    33  
    34  	opt := &SetupOptions{
    35  		IOStreams: run.Streams,
    36  	}
    37  
    38  	opt.Complete(f, nil)
    39  }
    40  
    41  // Test that setup run with --gimme-doggo command returns a default nickname
    42  func TestSetupGimmeDoggo(t *testing.T) {
    43  	run := NewTestRunner(t, "test_peer_gimme_doggo", "qri_test_gimme_doggo")
    44  	defer run.Delete()
    45  
    46  	actual := run.MustExec(t, "qri setup --gimme-doggo")
    47  	expect := "peru_swedish_vallhund\n"
    48  	if diff := cmp.Diff(expect, actual); diff != "" {
    49  		t.Errorf("unexpected (-want +got):\n%s", diff)
    50  	}
    51  }
    52  
    53  // NOTE: These tests below do not use the TestRunner. This is because the TestRunner creates
    54  // its own MockRepo, which removes the need to run `qri setup`. Since the whole idea here is
    55  // to test `qri setup`'s behavior, we cannot use that functionality.
    56  
    57  // Test that setup with no input will use the suggested username
    58  func TestSetupWithNoInput(t *testing.T) {
    59  	ctx := context.Background()
    60  	keyData := testkeys.GetKeyData(0)
    61  
    62  	qriHome := createTmpQriHome(t)
    63  	cmd, shutdown := newCommand(ctx, qriHome, repotest.NewTestCrypto())
    64  
    65  	cmdText := "qri setup"
    66  	if err := executeCommand(cmd, cmdText); err != nil {
    67  		timedShutdown(fmt.Sprintf("ExecCommand: %q\n", cmdText), shutdown)
    68  		t.Fatal(err)
    69  	}
    70  
    71  	configData := readConfigFile(t, qriHome)
    72  
    73  	username := configData["Profile"].(map[string]interface{})["peername"]
    74  	expect := "red_munsell_coton_de_tulear"
    75  	if username != expect {
    76  		t.Errorf("setup didn't create correct username, expect: %s, got: %s", expect, username)
    77  	}
    78  
    79  	profileID := configData["Profile"].(map[string]interface{})["id"]
    80  	if profileID != keyData.EncodedPeerID {
    81  		t.Errorf("setup didn't create correct profileID, expect: %s, got: %s", keyData.EncodedPeerID, profileID)
    82  	}
    83  
    84  	privkey := configData["Profile"].(map[string]interface{})["privkey"]
    85  	if privkey != keyData.EncodedPrivKey {
    86  		t.Errorf("setup didn't create correct private key")
    87  	}
    88  }
    89  
    90  // Test that setup with a response on stdin will use that username from stdin
    91  func TestSetupUsernameOnStdin(t *testing.T) {
    92  	ctx := context.Background()
    93  	keyData := testkeys.GetKeyData(0)
    94  
    95  	qriHome := createTmpQriHome(t)
    96  	stdinText := "qri_test_name"
    97  	cmd, shutdown := newCommandWithStdin(ctx, qriHome, stdinText, repotest.NewTestCrypto())
    98  
    99  	cmdText := "qri setup"
   100  	if err := executeCommand(cmd, cmdText); err != nil {
   101  		timedShutdown(fmt.Sprintf("ExecCommand: %q\n", cmdText), shutdown)
   102  		t.Fatal(err)
   103  	}
   104  
   105  	configData := readConfigFile(t, qriHome)
   106  
   107  	username := configData["Profile"].(map[string]interface{})["peername"]
   108  	expect := "qri_test_name"
   109  	if username != expect {
   110  		t.Errorf("setup didn't create correct username, expect: %s, got: %s", expect, username)
   111  	}
   112  
   113  	profileID := configData["Profile"].(map[string]interface{})["id"]
   114  	if profileID != keyData.EncodedPeerID {
   115  		t.Errorf("setup didn't create correct profileID, expect: %s, got: %s", keyData.EncodedPeerID, profileID)
   116  	}
   117  
   118  	privkey := configData["Profile"].(map[string]interface{})["privkey"]
   119  	if privkey != keyData.EncodedPrivKey {
   120  		t.Errorf("setup didn't create correct private key")
   121  	}
   122  }
   123  
   124  // Test that setup doesn't prompt if given anonymous flag
   125  func TestSetupAnonymousIgnoresStdin(t *testing.T) {
   126  	ctx := context.Background()
   127  	keyData := testkeys.GetKeyData(0)
   128  
   129  	qriHome := createTmpQriHome(t)
   130  	stdinText := "qri_test_name"
   131  	cmd, shutdown := newCommandWithStdin(ctx, qriHome, stdinText, repotest.NewTestCrypto())
   132  
   133  	cmdText := "qri setup --anonymous"
   134  	if err := executeCommand(cmd, cmdText); err != nil {
   135  		timedShutdown(fmt.Sprintf("ExecCommand: %q\n", cmdText), shutdown)
   136  		t.Fatal(err)
   137  	}
   138  
   139  	configData := readConfigFile(t, qriHome)
   140  
   141  	username := configData["Profile"].(map[string]interface{})["peername"]
   142  	expect := "red_munsell_coton_de_tulear"
   143  	if username != expect {
   144  		t.Errorf("setup didn't create correct username, expect: %s, got: %s", expect, username)
   145  	}
   146  
   147  	profileID := configData["Profile"].(map[string]interface{})["id"]
   148  	if profileID != keyData.EncodedPeerID {
   149  		t.Errorf("setup didn't create correct profileID, expect: %s, got: %s", keyData.EncodedPeerID, profileID)
   150  	}
   151  
   152  	privkey := configData["Profile"].(map[string]interface{})["privkey"]
   153  	if privkey != keyData.EncodedPrivKey {
   154  		t.Errorf("setup didn't create correct private key")
   155  	}
   156  }
   157  
   158  // Test that setup with the --username flag will use that value
   159  func TestSetupUsernameFlag(t *testing.T) {
   160  	ctx := context.Background()
   161  	keyData := testkeys.GetKeyData(0)
   162  
   163  	qriHome := createTmpQriHome(t)
   164  	cmd, shutdown := newCommand(ctx, qriHome, repotest.NewTestCrypto())
   165  
   166  	cmdText := "qri setup --username qri_cool_user"
   167  	if err := executeCommand(cmd, cmdText); err != nil {
   168  		timedShutdown(fmt.Sprintf("ExecCommand: %q\n", cmdText), shutdown)
   169  		t.Fatal(err)
   170  	}
   171  
   172  	configData := readConfigFile(t, qriHome)
   173  
   174  	username := configData["Profile"].(map[string]interface{})["peername"]
   175  	expect := "qri_cool_user"
   176  	if username != expect {
   177  		t.Errorf("setup didn't create correct username, expect: %s, got: %s", expect, username)
   178  	}
   179  
   180  	profileID := configData["Profile"].(map[string]interface{})["id"]
   181  	if profileID != keyData.EncodedPeerID {
   182  		t.Errorf("setup didn't create correct profileID, expect: %s, got: %s", keyData.EncodedPeerID, profileID)
   183  	}
   184  
   185  	privkey := configData["Profile"].(map[string]interface{})["privkey"]
   186  	if privkey != keyData.EncodedPrivKey {
   187  		t.Errorf("setup didn't create correct private key")
   188  	}
   189  }
   190  
   191  // Test that setup will fail if passed an invalid username from stdin
   192  func TestSetupInvalidStdinResponse(t *testing.T) {
   193  	ctx := context.Background()
   194  
   195  	qriHome := createTmpQriHome(t)
   196  	stdinText := "_not_valid_name"
   197  	cmd, shutdown := newCommandWithStdin(ctx, qriHome, stdinText, repotest.NewTestCrypto())
   198  
   199  	cmdText := "qri setup"
   200  	err := executeCommand(cmd, cmdText)
   201  	if err != nil {
   202  		timedShutdown(fmt.Sprintf("ExecCommand: %q\n", cmdText), shutdown)
   203  	}
   204  	if err == nil {
   205  		t.Fatal("expected setup to fail due to invalid username, did not get an error")
   206  	}
   207  	expectErr := `username must start with a lower-case letter, and only contain lower-case letters, numbers, dashes, and underscores`
   208  	if expectErr != err.Error() {
   209  		t.Errorf("error mismatch, expect %s, got %s", expectErr, err.Error())
   210  	}
   211  }
   212  
   213  // Test that setup will fail if passed an invalid username from the --username flag
   214  func TestSetupInvalidUsernameFlag(t *testing.T) {
   215  	ctx := context.Background()
   216  
   217  	qriHome := createTmpQriHome(t)
   218  	cmd, shutdown := newCommand(ctx, qriHome, repotest.NewTestCrypto())
   219  
   220  	cmdText := "qri setup --username InvalidUser"
   221  	err := executeCommand(cmd, cmdText)
   222  	if err != nil {
   223  		timedShutdown(fmt.Sprintf("ExecCommand: %q\n", cmdText), shutdown)
   224  	}
   225  	if err == nil {
   226  		t.Fatal("expected setup to fail due to invalid username, did not get an error")
   227  	}
   228  	expectErr := `username may not contain any upper-case letters`
   229  	if expectErr != err.Error() {
   230  		t.Errorf("error mismatch, expect %s, got %s", expectErr, err.Error())
   231  	}
   232  }
   233  
   234  // Test that setup with the QRI_SETUP_CONFIG_DATA envvar will use that username
   235  func TestSetupConfigData(t *testing.T) {
   236  	ctx := context.Background()
   237  	keyData := testkeys.GetKeyData(0)
   238  
   239  	qriHome := createTmpQriHome(t)
   240  	cmd, shutdown := newCommand(ctx, qriHome, repotest.NewTestCrypto())
   241  
   242  	os.Setenv("QRI_SETUP_CONFIG_DATA", `{"profile":{"peername":"qri_my_fav_user"}}`)
   243  	defer os.Setenv("QRI_SETUP_CONFIG_DATA", "")
   244  
   245  	cmdText := "qri setup"
   246  	if err := executeCommand(cmd, cmdText); err != nil {
   247  		timedShutdown(fmt.Sprintf("ExecCommand: %q\n", cmdText), shutdown)
   248  		t.Fatal(err)
   249  	}
   250  
   251  	configData := readConfigFile(t, qriHome)
   252  
   253  	username := configData["Profile"].(map[string]interface{})["peername"]
   254  	expect := "qri_my_fav_user"
   255  	if username != expect {
   256  		t.Errorf("setup didn't create correct username, expect: %s, got: %s", expect, username)
   257  	}
   258  
   259  	profileID := configData["Profile"].(map[string]interface{})["id"]
   260  	if profileID != keyData.EncodedPeerID {
   261  		t.Errorf("setup didn't create correct profileID, expect: %s, got: %s", keyData.EncodedPeerID, profileID)
   262  	}
   263  
   264  	privkey := configData["Profile"].(map[string]interface{})["privkey"]
   265  	if privkey != keyData.EncodedPrivKey {
   266  		t.Errorf("setup didn't create correct private key")
   267  	}
   268  }
   269  
   270  // Test that setup also works with real crypto
   271  func TestSetupRealCrypto(t *testing.T) {
   272  	ctx := context.Background()
   273  
   274  	qriHome := createTmpQriHome(t)
   275  	// NOTE: This test uses a real instance of the crypto generator. This will
   276  	// be noticably slower than the other tests, and will also act non-deterministically.
   277  	// Do not use this function in other tests, it is only used here to explicitly
   278  	// verify that it works in this one case.
   279  	cmd, shutdown := newCommand(ctx, qriHome, key.NewCryptoGenerator())
   280  
   281  	cmdText := "qri setup"
   282  	if err := executeCommand(cmd, cmdText); err != nil {
   283  		timedShutdown(fmt.Sprintf("ExecCommand: %q\n", cmdText), shutdown)
   284  		t.Fatal(err)
   285  	}
   286  
   287  	configData := readConfigFile(t, qriHome)
   288  
   289  	username := configData["Profile"].(map[string]interface{})["peername"].(string)
   290  	// NOTE: Shortest name I've ever seen is `peru_pug`
   291  	if len(username) < 8 {
   292  		t.Errorf("setup used real crypto, username seems wrong, got %s", username)
   293  	}
   294  
   295  	profileID := configData["Profile"].(map[string]interface{})["id"].(string)
   296  	if len(profileID) != 52 {
   297  		t.Errorf("setup used real crypto, profileID seems wrong, len is %d", len(profileID))
   298  	}
   299  
   300  	privkey := configData["Profile"].(map[string]interface{})["privkey"].(string)
   301  	if len(privkey) != 92 {
   302  		t.Errorf("setup used real crypto, privkey seems wrong, len is %d", len(privkey))
   303  	}
   304  }
   305  
   306  func createTmpQriHome(t *testing.T) string {
   307  	tmpPath, err := ioutil.TempDir("", "qri_test_setup")
   308  	if err != nil {
   309  		t.Fatal(err)
   310  	}
   311  
   312  	qriHome := filepath.Join(tmpPath, "qri_home")
   313  	err = os.MkdirAll(qriHome, 0755)
   314  	if err != nil {
   315  		t.Fatal(err)
   316  	}
   317  
   318  	err = os.Setenv("QRI_PATH", qriHome)
   319  	if err != nil {
   320  		t.Fatal(err)
   321  	}
   322  
   323  	// Clear this envvar in order to get tests to pass on continuous build. If this
   324  	// envvar is set, it will override other configuration.
   325  	err = os.Setenv("QRI_SETUP_CONFIG_DATA", "")
   326  	if err != nil {
   327  		t.Fatal(err)
   328  	}
   329  
   330  	return qriHome
   331  }
   332  
   333  func readConfigFile(t *testing.T, path string) map[string]interface{} {
   334  	configContents, err := ioutil.ReadFile(filepath.Join(path, "config.yaml"))
   335  	if err != nil {
   336  		t.Fatal(err)
   337  	}
   338  
   339  	configData := make(map[string]interface{})
   340  	err = yaml.Unmarshal(configContents, &configData)
   341  	if err != nil {
   342  		t.Fatal(err)
   343  	}
   344  
   345  	return configData
   346  }
   347  
   348  func newCommand(ctx context.Context, path string, generator key.CryptoGenerator) (*cobra.Command, func() <-chan error) {
   349  	return newCommandWithStdin(ctx, path, "", generator)
   350  }
   351  
   352  func newCommandWithStdin(ctx context.Context, path, stdinText string, generator key.CryptoGenerator) (*cobra.Command, func() <-chan error) {
   353  	streams, in, _, _ := ioes.NewTestIOStreams()
   354  	if stdinText != "" {
   355  		in.WriteString(stdinText)
   356  	}
   357  	ctors := Constructors{
   358  		CryptoGenerator: generator,
   359  		InitIPFS:        repotest.InitIPFSRepo,
   360  	}
   361  	cmd, shutdown := NewQriCommand(ctx, path, ctors, streams)
   362  	cmd.SetOutput(streams.Out)
   363  	return cmd, shutdown
   364  }