1// Copyright 2018 Keybase, Inc. All rights reserved. Use of 2// this source code is governed by the included BSD license. 3 4package engine 5 6import ( 7 "encoding/hex" 8 "testing" 9 10 "github.com/keybase/client/go/libkb" 11 "github.com/stretchr/testify/require" 12) 13 14func persistDeviceCloneState(m libkb.MetaContext, d libkb.DeviceCloneState) error { 15 return libkb.SetDeviceCloneState(m, d) 16} 17 18func runAndGetDeviceCloneState(m libkb.MetaContext) (d libkb.DeviceCloneState, err error) { 19 _, _, err = libkb.UpdateDeviceCloneState(m) 20 if err != nil { 21 return d, err 22 } 23 d, _ = libkb.GetDeviceCloneState(m) 24 return d, err 25} 26 27func assertIsValidToken(tc libkb.TestContext, token string) { 28 _, err := hex.DecodeString(token) 29 require.NoError(tc.T, err) 30 require.Equal(tc.T, len(token), 32) 31} 32 33func assertSuccessfulRun(tc libkb.TestContext, d libkb.DeviceCloneState, err error) { 34 require.NoError(tc.T, err) 35 require.Equal(tc.T, d.Stage, "") 36 assertIsValidToken(tc, d.Prior) 37} 38 39func TestDeviceCloneStateFirstRun(t *testing.T) { 40 tc := SetupEngineTest(t, "DeviceCloneState") 41 defer tc.Cleanup() 42 _ = CreateAndSignupFakeUser(tc, "fu") 43 m := NewMetaContextForTest(tc) 44 45 d, err := runAndGetDeviceCloneState(m) 46 assertSuccessfulRun(tc, d, err) 47 require.Equal(tc.T, d.Clones, 1) 48} 49 50func TestDeviceCloneStateSuccessfulUpdate(t *testing.T) { 51 tc := SetupEngineTest(t, "DeviceCloneState") 52 defer tc.Cleanup() 53 _ = CreateAndSignupFakeUser(tc, "fu") 54 m := NewMetaContextForTest(tc) 55 //setup: perform an initial run 56 d0, err := runAndGetDeviceCloneState(m) 57 require.NoError(tc.T, err) 58 59 d, err := runAndGetDeviceCloneState(m) 60 assertSuccessfulRun(tc, d, err) 61 require.NotEqual(tc.T, d.Prior, d0.Prior) 62 require.Equal(tc.T, d.Clones, 1) 63} 64 65func TestDeviceCloneStateRecoveryFromFailureBeforeServer(t *testing.T) { 66 tc := SetupEngineTest(t, "DeviceCloneState") 67 defer tc.Cleanup() 68 _ = CreateAndSignupFakeUser(tc, "fu") 69 m := NewMetaContextForTest(tc) 70 // setup: persist tokens as if the process failed 71 // before the server received the update 72 d0 := libkb.DeviceCloneState{ 73 Prior: libkb.DefaultCloneTokenValue, 74 Stage: "22222222222222222222222222222222", 75 Clones: 1, 76 } 77 err := persistDeviceCloneState(m, d0) 78 require.NoError(t, err) 79 80 d, err := runAndGetDeviceCloneState(m) 81 assertSuccessfulRun(tc, d, err) 82 require.Equal(tc.T, d.Prior, d0.Stage) 83 require.Equal(tc.T, d.Clones, 1) 84} 85 86func TestDeviceCloneStateRecoveryFromFailureAfterServer(t *testing.T) { 87 tc := SetupEngineTest(t, "DeviceCloneState") 88 defer tc.Cleanup() 89 _ = CreateAndSignupFakeUser(tc, "fu") 90 m := NewMetaContextForTest(tc) 91 // setup: run twice. then reset the persistence to where it would have been 92 // if the server got the second update but did not ack it successfully to the client. 93 d0, err := runAndGetDeviceCloneState(m) 94 require.NoError(t, err) 95 d1, err := runAndGetDeviceCloneState(m) 96 require.NoError(t, err) 97 tmp := libkb.DeviceCloneState{Prior: d0.Prior, Stage: d1.Prior, Clones: 1} 98 err = persistDeviceCloneState(m, tmp) 99 require.NoError(t, err) 100 101 d, err := runAndGetDeviceCloneState(m) 102 assertSuccessfulRun(tc, d, err) 103 require.Equal(tc.T, d.Prior, d1.Prior) 104 require.Equal(tc.T, d.Clones, 1) 105} 106 107func TestDeviceCloneStateCloneDetected(t *testing.T) { 108 tc := SetupEngineTest(t, "DeviceCloneState") 109 defer tc.Cleanup() 110 _ = CreateAndSignupFakeUser(tc, "fu") 111 m := NewMetaContextForTest(tc) 112 // setup: perform two runs, and then manually persist the earlier 113 // prior token to simulate a subsequent run by a cloned device 114 d0, err := runAndGetDeviceCloneState(m) 115 require.NoError(tc.T, err) 116 _, err = runAndGetDeviceCloneState(m) 117 require.NoError(tc.T, err) 118 err = persistDeviceCloneState(m, d0) 119 require.NoError(t, err) 120 121 before, after, err := libkb.UpdateDeviceCloneState(m) 122 require.NoError(t, err) 123 124 d, err := libkb.GetDeviceCloneState(m) 125 assertSuccessfulRun(tc, d, err) 126 require.NotEqual(tc.T, d.Prior, d0.Stage, "despite there being a clone, the prior still needs to change") 127 require.Equal(tc.T, d.Clones, 2) 128 require.Equal(tc.T, before, 1, "there was one clone before the test run") 129 require.Equal(tc.T, after, 2, "there were two clones after the test run") 130} 131 132func TestDeviceCloneStateBeforeAndAfterOnFirstRun(t *testing.T) { 133 tc := SetupEngineTest(t, "DeviceCloneState") 134 defer tc.Cleanup() 135 _ = CreateAndSignupFakeUser(tc, "fu") 136 m := NewMetaContextForTest(tc) 137 138 before, after, err := libkb.UpdateDeviceCloneState(m) 139 require.NoError(tc.T, err) 140 require.Equal(tc.T, before, 1, "there was one clone before the test run") 141 require.Equal(tc.T, after, 1, "there was one clone after the test run") 142} 143