github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/systests/rpc_test.go (about) 1 // Copyright 2015 Keybase, Inc. All rights reserved. Use of 2 // this source code is governed by the included BSD license. 3 4 package systests 5 6 // Test various RPCs that are used mainly in other clients but not by the CLI. 7 8 import ( 9 "fmt" 10 "regexp" 11 "sort" 12 "strings" 13 "sync" 14 "testing" 15 16 "github.com/davecgh/go-spew/spew" 17 "github.com/keybase/client/go/client" 18 "github.com/keybase/client/go/gregor" 19 "github.com/keybase/client/go/libkb" 20 "github.com/keybase/client/go/protocol/gregor1" 21 keybase1 "github.com/keybase/client/go/protocol/keybase1" 22 service "github.com/keybase/client/go/service" 23 "github.com/keybase/client/go/teams" 24 "github.com/keybase/go-framed-msgpack-rpc/rpc" 25 "github.com/stretchr/testify/require" 26 context "golang.org/x/net/context" 27 ) 28 29 type fakeConnectivityMonitor struct { 30 sync.Mutex 31 res libkb.ConnectivityMonitorResult 32 } 33 34 func (f *fakeConnectivityMonitor) IsConnected(ctx context.Context) libkb.ConnectivityMonitorResult { 35 f.Lock() 36 defer f.Unlock() 37 return f.res 38 } 39 40 func (f *fakeConnectivityMonitor) Set(r libkb.ConnectivityMonitorResult) { 41 f.Lock() 42 defer f.Unlock() 43 f.res = r 44 } 45 46 func (f *fakeConnectivityMonitor) CheckReachability(ctx context.Context) error { 47 return nil 48 } 49 50 func TestRPCs(t *testing.T) { 51 tc := setupTest(t, "rpcs") 52 defer tc.Cleanup() 53 54 tc2 := cloneContext(tc) 55 defer tc2.Cleanup() 56 57 // Set a connectivity manager we can control, and set NoGregor so that 58 // way it's not overwritten when we startup gregor. 59 fcm := fakeConnectivityMonitor{} 60 fcm.Set(libkb.ConnectivityMonitorYes) 61 tc.G.ConnectivityMonitor = &fcm 62 tc.G.Env.Test.NoGregor = true 63 64 stopCh := make(chan error) 65 svc := service.NewService(tc.G, false) 66 startCh := svc.GetStartChannel() 67 go func() { 68 err := svc.Run() 69 if err != nil { 70 t.Logf("Running the service produced an error: %v", err) 71 } 72 stopCh <- err 73 }() 74 75 <-startCh 76 77 // Add test RPC methods here. 78 stage := func(label string) { 79 t.Logf("> Stage: %v", label) 80 } 81 stage("testIdentifyResolve3") 82 testIdentifyResolve3(t, tc2.G) 83 stage("testCheckInvitationCode") 84 testCheckInvitationCode(t, tc2.G) 85 stage("testLoadAllPublicKeysUnverified") 86 testLoadAllPublicKeysUnverified(t, tc2.G) 87 stage("testLoadUserWithNoKeys") 88 testLoadUserWithNoKeys(t, tc2.G) 89 stage("test LoadUserPlusKeysV2") 90 testLoadUserPlusKeysV2(t, tc2.G) 91 stage("testCheckDevicesForUser") 92 testCheckDevicesForUser(t, tc2.G) 93 stage("testIdentify2") 94 testIdentify2(t, tc2.G) 95 stage("testMerkle") 96 testMerkle(t, tc2.G) 97 stage("testConfig") 98 testConfig(t, tc2.G) 99 stage("testResolve3Offline") 100 testResolve3Offline(t, tc2.G, &fcm) 101 stage("testLoadUserPlusKeysV2Offline") 102 testLoadUserPlusKeysV2Offline(t, tc2.G, &fcm) 103 stage("testGetUpdateInfo2") 104 testGetUpdateInfo2(t, tc2.G) 105 106 if err := CtlStop(tc2.G); err != nil { 107 t.Fatal(err) 108 } 109 110 // If the server failed, it's also an error 111 if err := <-stopCh; err != nil { 112 t.Fatal(err) 113 } 114 } 115 116 func testResolve3Offline(t *testing.T, g *libkb.GlobalContext, fcm *fakeConnectivityMonitor) { 117 cli, err := client.GetIdentifyClient(g) 118 require.NoError(t, err) 119 fetch := func() { 120 arg := keybase1.Resolve3Arg{Assertion: "uid:eb72f49f2dde6429e5d78003dae0c919", Oa: keybase1.OfflineAvailability_BEST_EFFORT} 121 res, err := cli.Resolve3(context.TODO(), arg) 122 require.NoError(t, err) 123 require.Equal(t, "t_tracy", res.Name) 124 } 125 fetchFail := func(expectedError error) { 126 arg := keybase1.Resolve3Arg{Assertion: "no_such_user_yo", Oa: keybase1.OfflineAvailability_BEST_EFFORT} 127 _, err = cli.Resolve3(context.TODO(), arg) 128 require.Error(t, err) 129 require.IsType(t, expectedError, err) 130 } 131 132 fetch() 133 fcm.Set(libkb.ConnectivityMonitorNo) 134 fetch() 135 fetchFail(libkb.OfflineError{}) 136 fcm.Set(libkb.ConnectivityMonitorYes) 137 fetch() 138 fetchFail(libkb.NotFoundError{}) 139 } 140 141 func testIdentifyResolve3(t *testing.T, g *libkb.GlobalContext) { 142 143 cli, err := client.GetIdentifyClient(g) 144 if err != nil { 145 t.Fatalf("failed to get new identifyclient: %v", err) 146 } 147 148 // We don't want to hit the cache, since the previous lookup never hit the 149 // server. For Resolve3, we have to, since we need a username. So test that 150 // here. 151 if res, err := cli.Resolve3(context.TODO(), keybase1.Resolve3Arg{Assertion: "uid:eb72f49f2dde6429e5d78003dae0c919"}); err != nil { 152 t.Fatalf("Resolve failed: %v\n", err) 153 } else if res.Name != "t_tracy" { 154 t.Fatalf("Wrong username: %s != 't_tracy", res.Name) 155 } 156 157 if res, err := cli.Resolve3(context.TODO(), keybase1.Resolve3Arg{Assertion: "t_tracy@rooter"}); err != nil { 158 t.Fatalf("Resolve3 failed: %v\n", err) 159 } else if res.Name != "t_tracy" { 160 t.Fatalf("Wrong name: %s != 't_tracy", res.Name) 161 } else if !res.Id.AsUserOrBust().Equal(keybase1.UID("eb72f49f2dde6429e5d78003dae0c919")) { 162 t.Fatalf("Wrong uid for tracy: %s\n", res.Id) 163 } 164 165 if _, err := cli.Resolve3(context.TODO(), keybase1.Resolve3Arg{Assertion: "foobag@rooter"}); err == nil { 166 t.Fatalf("expected an error on a bad resolve, but got none") 167 } else if _, ok := err.(libkb.ResolutionError); !ok { 168 t.Fatalf("Wrong error: wanted type %T but got (%v, %T)", libkb.ResolutionError{}, err, err) 169 } 170 171 if res, err := cli.Resolve3(context.TODO(), keybase1.Resolve3Arg{Assertion: "t_tracy"}); err != nil { 172 t.Fatalf("Resolve3 failed: %v\n", err) 173 } else if res.Name != "t_tracy" { 174 t.Fatalf("Wrong name: %s != 't_tracy", res.Name) 175 } else if !res.Id.AsUserOrBust().Equal(keybase1.UID("eb72f49f2dde6429e5d78003dae0c919")) { 176 t.Fatalf("Wrong uid for tracy: %s\n", res.Id) 177 } 178 } 179 180 func testCheckInvitationCode(t *testing.T, g *libkb.GlobalContext) { 181 cli, err := client.GetSignupClient(g) 182 if err != nil { 183 t.Fatalf("failed to get a signup client: %v", err) 184 } 185 186 err = cli.CheckInvitationCode(context.TODO(), keybase1.CheckInvitationCodeArg{InvitationCode: libkb.TestInvitationCode}) 187 if err != nil { 188 t.Fatalf("Did not expect an error code, but got: %v", err) 189 } 190 err = cli.CheckInvitationCode(context.TODO(), keybase1.CheckInvitationCodeArg{InvitationCode: "eeoeoeoe333o3"}) 191 if _, ok := err.(libkb.BadInvitationCodeError); !ok { 192 t.Fatalf("Expected an error code, but got %T %v", err, err) 193 } 194 } 195 196 func testLoadAllPublicKeysUnverified(t *testing.T, g *libkb.GlobalContext) { 197 198 cli, err := client.GetUserClient(g) 199 if err != nil { 200 t.Fatalf("failed to get user client: %s", err) 201 } 202 203 // t_rosetta 204 arg := keybase1.LoadAllPublicKeysUnverifiedArg{Uid: keybase1.UID("b8939251cb3d367e68587acb33a64d19")} 205 res, err := cli.LoadAllPublicKeysUnverified(context.TODO(), arg) 206 if err != nil { 207 t.Fatalf("failed to make load keys call: %s", err) 208 } 209 210 if len(res) != 3 { 211 t.Fatalf("wrong amount of keys loaded: %d != %d", len(res), 3) 212 } 213 214 keys := map[keybase1.KID]bool{ 215 keybase1.KID("0101fe1183765f256289427d6943cd8bab3b5fe095bcdd27f031ed298da523efd3120a"): true, 216 keybase1.KID("0101b5839c4ccaa9d03b3016b9aa73a7e3eafb067f9c86c07a6f2f79cb8558b1c97f0a"): true, 217 keybase1.KID("0101188ee7e63ccbd05af498772ab2975ee29df773240d17dde09aecf6c132a5a9a60a"): true, 218 } 219 220 for _, key := range res { 221 if _, ok := keys[key.KID]; !ok { 222 t.Fatalf("unknown key in response: %s", key.KID) 223 } 224 } 225 } 226 227 func testLoadUserPlusKeysV2Offline(t *testing.T, g *libkb.GlobalContext, fcm *fakeConnectivityMonitor) { 228 cli, err := client.GetUserClient(g) 229 require.NoError(t, err) 230 231 kid := keybase1.KID("012012a40a6b77a9de5e48922262870565900f5689e179761ea8c8debaa586bfd0090a") 232 uid := keybase1.UID("359c7644857203be38bfd3bf79bf1819") 233 234 fetch := func() { 235 arg := keybase1.LoadUserPlusKeysV2Arg{ 236 Uid: uid, 237 PollForKID: kid, 238 Oa: keybase1.OfflineAvailability_BEST_EFFORT, 239 } 240 frank, err := cli.LoadUserPlusKeysV2(context.TODO(), arg) 241 require.NoError(t, err) 242 require.NotNil(t, frank) 243 require.Equal(t, len(frank.PastIncarnations), 0) 244 require.Equal(t, frank.Current.Username, "t_frank") 245 _, found := frank.Current.DeviceKeys[kid] 246 require.True(t, found) 247 require.Nil(t, frank.Current.Reset) 248 } 249 fetchFail := func(expectedError error) { 250 arg := keybase1.LoadUserPlusKeysV2Arg{ 251 Uid: "00000000000000000000000000000000", 252 Oa: keybase1.OfflineAvailability_BEST_EFFORT, 253 } 254 _, err := cli.LoadUserPlusKeysV2(context.TODO(), arg) 255 require.Error(t, err) 256 require.IsType(t, expectedError, err) 257 } 258 259 fetch() 260 fcm.Set(libkb.ConnectivityMonitorNo) 261 fetch() 262 fetchFail(libkb.OfflineError{}) 263 fcm.Set(libkb.ConnectivityMonitorYes) 264 fetch() 265 fetchFail(libkb.NotFoundError{}) 266 } 267 268 func testLoadUserPlusKeysV2(t *testing.T, g *libkb.GlobalContext) { 269 cli, err := client.GetUserClient(g) 270 if err != nil { 271 t.Fatalf("failed to get a user client: %v", err) 272 } 273 274 kid := keybase1.KID("012012a40a6b77a9de5e48922262870565900f5689e179761ea8c8debaa586bfd0090a") 275 uid := keybase1.UID("359c7644857203be38bfd3bf79bf1819") 276 277 frank, err := cli.LoadUserPlusKeysV2(context.TODO(), keybase1.LoadUserPlusKeysV2Arg{Uid: uid, PollForKID: kid}) 278 require.NoError(t, err) 279 require.NotNil(t, frank) 280 require.Equal(t, len(frank.PastIncarnations), 0) 281 require.Equal(t, frank.Current.Username, "t_frank") 282 _, found := frank.Current.DeviceKeys[kid] 283 require.True(t, found) 284 require.Nil(t, frank.Current.Reset) 285 } 286 287 func testLoadUserWithNoKeys(t *testing.T, g *libkb.GlobalContext) { 288 // The LoadUser class in libkb returns an error by default if the user in 289 // question has no keys. The RPC methods that wrap it should suppress this 290 // error, by setting the PublicKeyOptional flag. 291 292 cli, err := client.GetUserClient(g) 293 if err != nil { 294 t.Fatalf("failed to get a user client: %v", err) 295 } 296 297 // Check the LoadUserByName RPC. t_ellen is a test user with no keys. 298 loadUserByNameArg := keybase1.LoadUserByNameArg{Username: "t_ellen"} 299 tEllen, err := cli.LoadUserByName(context.TODO(), loadUserByNameArg) 300 if err != nil { 301 t.Fatal(err) 302 } 303 if tEllen.Username != "t_ellen" { 304 t.Fatalf("expected t_ellen, saw %s", tEllen.Username) 305 } 306 307 // Check the LoadUser RPC. 308 loadUserArg := keybase1.LoadUserArg{Uid: tEllen.Uid} 309 tEllen2, err := cli.LoadUser(context.TODO(), loadUserArg) 310 if err != nil { 311 t.Fatal(err) 312 } 313 if tEllen2.Username != "t_ellen" { 314 t.Fatalf("expected t_ellen, saw %s", tEllen2.Username) 315 } 316 } 317 318 func testCheckDevicesForUser(t *testing.T, g *libkb.GlobalContext) { 319 cli, err := client.GetDeviceClient(g) 320 if err != nil { 321 t.Fatalf("failed to get a device client: %v", err) 322 } 323 err = cli.CheckDeviceNameForUser(context.TODO(), keybase1.CheckDeviceNameForUserArg{ 324 Username: "t_frank", 325 Devicename: "bad $ device $ name", 326 }) 327 if _, ok := err.(libkb.DeviceBadNameError); !ok { 328 t.Fatalf("wanted a bad device name error; got %v", err) 329 } 330 err = cli.CheckDeviceNameForUser(context.TODO(), keybase1.CheckDeviceNameForUserArg{ 331 Username: "t_frank", 332 Devicename: "go c lient", 333 }) 334 if _, ok := err.(libkb.DeviceNameInUseError); !ok { 335 t.Fatalf("wanted a name in use error; got %v", err) 336 } 337 } 338 339 func testIdentify2(t *testing.T, g *libkb.GlobalContext) { 340 341 cli, err := client.GetIdentifyClient(g) 342 if err != nil { 343 t.Fatalf("failed to get new identifyclient: %v", err) 344 } 345 346 _, err = cli.Identify2(context.TODO(), keybase1.Identify2Arg{ 347 UserAssertion: "t_alice", 348 IdentifyBehavior: keybase1.TLFIdentifyBehavior_CHAT_GUI, 349 }) 350 if err != nil { 351 t.Fatalf("Identify2 failed: %v\n", err) 352 } 353 354 _, err = cli.Identify2(context.TODO(), keybase1.Identify2Arg{ 355 UserAssertion: "t_weriojweroi", 356 IdentifyBehavior: keybase1.TLFIdentifyBehavior_CHAT_GUI, 357 }) 358 if _, ok := err.(libkb.NotFoundError); !ok { 359 t.Fatalf("Expected a not-found error, but got: %v (%T)", err, err) 360 } 361 } 362 363 func testMerkle(t *testing.T, g *libkb.GlobalContext) { 364 365 cli, err := client.GetMerkleClient(g) 366 if err != nil { 367 t.Fatalf("failed to get new merkle client: %v", err) 368 } 369 370 root, err := cli.GetCurrentMerkleRoot(context.TODO(), int(-1)) 371 if err != nil { 372 t.Fatalf("GetCurrentMerkleRoot failed: %v\n", err) 373 } 374 if root.Root.Seqno <= keybase1.Seqno(0) { 375 t.Fatalf("Failed basic sanity check") 376 } 377 } 378 379 func testConfig(t *testing.T, g *libkb.GlobalContext) { 380 381 cli, err := client.GetConfigClient(g) 382 if err != nil { 383 t.Fatalf("failed to get new config client: %v", err) 384 } 385 config, err := cli.GetConfig(context.TODO(), 0) 386 if err != nil { 387 t.Fatal(err) 388 } 389 if config.ServerURI == "" { 390 t.Fatal("No service URI") 391 } 392 } 393 394 func testGetUpdateInfo2(t *testing.T, g *libkb.GlobalContext) { 395 cli, err := client.GetConfigClient(g) 396 require.NoError(t, err) 397 res, err := cli.GetUpdateInfo2(context.TODO(), keybase1.GetUpdateInfo2Arg{}) 398 require.NoError(t, err) 399 status, err := res.Status() 400 require.NoError(t, err) 401 require.Equal(t, keybase1.UpdateInfoStatus2_OK, status) 402 platform := "ios" 403 version := "0.0.1" 404 res, err = cli.GetUpdateInfo2(context.TODO(), keybase1.GetUpdateInfo2Arg{Platform: &platform, Version: &version}) 405 require.NoError(t, err) 406 status, err = res.Status() 407 require.NoError(t, err) 408 require.Equal(t, keybase1.UpdateInfoStatus2_CRITICAL, status) 409 require.IsType(t, "foo", res.Critical().Message) 410 require.True(t, len(res.Critical().Message) > 10) 411 } 412 413 type FakeGregorState struct { 414 items []gregor.Item 415 } 416 417 func (s FakeGregorState) Items() ([]gregor.Item, error) { 418 return s.items, nil 419 } 420 func (s FakeGregorState) GetItem(msgID gregor.MsgID) (i gregor.Item, r bool) { return } 421 func (s FakeGregorState) ItemsInCategory(c gregor.Category) (i []gregor.Item, e error) { 422 return 423 } 424 func (s FakeGregorState) ItemsWithCategoryPrefix(c gregor.Category) (i []gregor.Item, r error) { 425 return 426 } 427 func (s FakeGregorState) Marshal() (b []byte, e error) { return } 428 func (s FakeGregorState) Hash() (b []byte, e error) { return } 429 func (s FakeGregorState) Export() (p gregor.ProtocolState, e error) { return } 430 431 func buildGregorItem(category, deviceID, msgID string) gregor.Item { 432 imd := gregor1.ItemAndMetadata{ 433 Md_: &gregor1.Metadata{ 434 MsgID_: gregor1.MsgID([]byte(msgID)), 435 }, 436 Item_: &gregor1.Item{ 437 Category_: gregor1.Category(category), 438 Body_: gregor1.Body(fmt.Sprintf(`{"device_id": "%s"}`, deviceID)), 439 }, 440 } 441 return gregor.Item(imd) 442 } 443 444 func TestDismissDeviceChangeNotifications(t *testing.T) { 445 tc := setupTest(t, "ddcn") 446 mctx := libkb.NewMetaContextForTest(*tc) 447 448 dismisser := &libkb.FakeGregorState{} 449 exceptedDeviceID := "active-device-id" 450 state := &FakeGregorState{ 451 items: []gregor.Item{ 452 buildGregorItem("anything.else", "a-device-id", "not-dismissable-1"), 453 buildGregorItem("device.new", exceptedDeviceID, "not-dismissable-2"), 454 buildGregorItem("device.new", "a-device-id", "dismissable-1"), 455 buildGregorItem("device.revoked", "another-device-id", "dismissable-2"), 456 }, 457 } 458 expectedDismissedIDs := []gregor.MsgID{ 459 gregor1.MsgID("dismissable-1"), 460 gregor1.MsgID("dismissable-2"), 461 } 462 require.Equal(t, []gregor.MsgID(nil), dismisser.PeekDismissedIDs()) 463 err := service.LoopAndDismissForDeviceChangeNotifications(mctx, dismisser, 464 state, exceptedDeviceID) 465 require.NoError(t, err) 466 require.Equal(t, expectedDismissedIDs, dismisser.PeekDismissedIDs()) 467 } 468 469 func idLiteArg(id keybase1.UserOrTeamID, assertion string) keybase1.IdentifyLiteArg { 470 return keybase1.IdentifyLiteArg{ 471 Id: id, 472 Assertion: assertion, 473 IdentifyBehavior: keybase1.TLFIdentifyBehavior_CHAT_CLI, 474 } 475 } 476 477 func TestIdentifyLite(t *testing.T) { 478 tt := newTeamTester(t) 479 defer tt.cleanup() 480 481 tt.addUser("abc") 482 teamName := tt.users[0].createTeam() 483 g := tt.users[0].tc.G 484 485 t.Logf("make a team") 486 team, err := GetTeamForTestByStringName(context.Background(), g, teamName) 487 require.NoError(t, err) 488 489 getTeamName := func(teamID keybase1.TeamID) keybase1.TeamName { 490 team, err := teams.Load(context.Background(), g, keybase1.LoadTeamArg{ 491 ID: teamID, 492 }) 493 require.NoError(t, err) 494 return team.Name() 495 } 496 497 t.Logf("make an implicit team") 498 iTeamCreateName := strings.Join([]string{tt.users[0].username, "bob@github"}, ",") 499 iTeam, _, _, err := teams.LookupOrCreateImplicitTeam(context.TODO(), g, iTeamCreateName, false /*isPublic*/) 500 require.NoError(t, err) 501 iTeamImpName := getTeamName(iTeam.ID) 502 require.True(t, iTeamImpName.IsImplicit()) 503 require.NoError(t, err) 504 505 cli, err := client.GetIdentifyClient(g) 506 require.NoError(t, err, "failed to get new identifyclient") 507 508 // test ok assertions 509 var units = []struct { 510 assertion string 511 resID keybase1.TeamID 512 resName string 513 }{ 514 { 515 assertion: "t_alice", 516 resName: "t_alice", 517 }, { 518 assertion: "team:" + teamName, 519 resID: team.ID, 520 resName: teamName, 521 }, { 522 assertion: "tid:" + team.ID.String(), 523 resID: team.ID, 524 resName: teamName, 525 }, 526 } 527 for _, unit := range units { 528 res, err := cli.IdentifyLite(context.Background(), idLiteArg("", unit.assertion)) 529 require.NoError(t, err, "IdentifyLite (%s) failed", unit.assertion) 530 531 if len(unit.resID) > 0 { 532 require.Equal(t, unit.resID.String(), res.Ul.Id.String()) 533 } 534 535 if len(unit.resName) > 0 { 536 require.Equal(t, unit.resName, res.Ul.Name) 537 } 538 } 539 540 // test identify by assertion and id 541 assertions := []string{"team:" + teamName, "tid:" + team.ID.String()} 542 for _, assertion := range assertions { 543 _, err := cli.IdentifyLite(context.Background(), idLiteArg(team.ID.AsUserOrTeam(), assertion)) 544 require.NoError(t, err, "IdentifyLite by assertion and id (%s)", assertion) 545 } 546 547 // test identify by id only 548 _, err = cli.IdentifyLite(context.Background(), idLiteArg(team.ID.AsUserOrTeam(), "")) 549 require.NoError(t, err, "IdentifyLite id only") 550 551 // test invalid user format 552 _, err = cli.IdentifyLite(context.Background(), idLiteArg("", "__t_alice")) 553 require.Error(t, err) 554 require.Contains(t, err.Error(), "bad keybase username") 555 556 // test team read error 557 assertions = []string{"team:jwkj22111z"} 558 for _, assertion := range assertions { 559 _, err := cli.IdentifyLite(context.Background(), idLiteArg("", assertion)) 560 aerr, ok := err.(libkb.AppStatusError) 561 if ok { 562 if aerr.Code != libkb.SCTeamNotFound { 563 t.Fatalf("app status code: %d, expected %d", aerr.Code, libkb.SCTeamNotFound) 564 } 565 } else { 566 require.True(t, regexp.MustCompile("Team .* does not exist").MatchString(err.Error()), 567 "Expected an AppStatusError or team-does-not-exist for %s, but got: %v (%T)", assertion, err, err) 568 } 569 } 570 571 // test not found assertions 572 assertions = []string{"t_weriojweroi"} 573 for _, assertion := range assertions { 574 _, err := cli.IdentifyLite(context.Background(), idLiteArg("", assertion)) 575 if _, ok := err.(libkb.NotFoundError); !ok { 576 t.Fatalf("assertion %s, error: %s (%T), expected libkb.NotFoundError", assertion, err, err) 577 } 578 } 579 } 580 581 // test ResolveIdentifyImplicitTeam with a social assertion 582 func TestResolveIdentifyImplicitTeamWithSocial(t *testing.T) { 583 tt := newTeamTester(t) 584 defer tt.cleanup() 585 586 tt.addUser("abc") 587 g := tt.users[0].tc.G 588 589 tt.addUser("wong") 590 wong := tt.users[1] 591 wong.proveRooter() 592 593 getTeamName := func(teamID keybase1.TeamID) keybase1.TeamName { 594 team, err := teams.Load(context.Background(), g, keybase1.LoadTeamArg{ 595 ID: teamID, 596 }) 597 require.NoError(t, err) 598 return team.Name() 599 } 600 601 iTeamNameCreate := strings.Join([]string{"bob@github", tt.users[0].username, wong.username}, ",") 602 // lookup with an assertion 603 iTeamNameLookup := strings.Join([]string{"bob@github", tt.users[0].username, wong.username + "@rooter"}, ",") 604 // the returned name should be sorted with the logged-in user first 605 iTeamNameSorted := strings.Join([]string{tt.users[0].username, "bob@github", wong.username}, ",") 606 607 t.Logf("make an implicit team") 608 iTeam, _, _, err := teams.LookupOrCreateImplicitTeam(context.TODO(), g, iTeamNameCreate, false /*isPublic*/) 609 require.NoError(t, err) 610 iTeamImpName := getTeamName(iTeam.ID) 611 require.True(t, iTeamImpName.IsImplicit()) 612 613 cli, err := client.GetIdentifyClient(g) 614 require.NoError(t, err, "failed to get new identifyclient") 615 616 res, err := cli.ResolveIdentifyImplicitTeam(context.Background(), keybase1.ResolveIdentifyImplicitTeamArg{ 617 Assertions: iTeamNameLookup, 618 Suffix: "", 619 IsPublic: false, 620 DoIdentifies: false, 621 Create: true, 622 IdentifyBehavior: keybase1.TLFIdentifyBehavior_DEFAULT_KBFS, 623 }) 624 require.NoError(t, err) 625 require.Equal(t, res.DisplayName, iTeamNameSorted) 626 require.Equal(t, res.TeamID, iTeam.ID) 627 require.True(t, compareUserVersionSets([]keybase1.UserVersion{tt.users[0].userVersion(), wong.userVersion()}, res.Writers)) 628 require.Nil(t, res.TrackBreaks, "track breaks") 629 } 630 631 // test ResolveIdentifyImplicitTeam with readers (also with DoIdentifies) 632 func TestResolveIdentifyImplicitTeamWithReaders(t *testing.T) { 633 tt := newTeamTester(t) 634 defer tt.cleanup() 635 636 tt.addUser("abc") 637 g := tt.users[0].tc.G 638 639 tt.addUser("wong") 640 wong := tt.users[1] 641 wong.proveRooter() 642 643 iTeamNameCreate := tt.users[0].username + "#" + strings.Join([]string{"bob@github", wong.username}, ",") 644 // lookup with an assertion 645 iTeamNameLookup := tt.users[0].username + "#" + strings.Join([]string{"bob@github", wong.username + "@rooter"}, ",") 646 647 t.Logf("make an implicit team") 648 iTeam, _, _, err := teams.LookupOrCreateImplicitTeam(context.TODO(), g, iTeamNameCreate, false /*isPublic*/) 649 require.NoError(t, err) 650 651 cli, err := client.GetIdentifyClient(g) 652 require.NoError(t, err, "failed to get new identifyclient") 653 attachIdentifyUI(t, g, newSimpleIdentifyUI()) 654 655 res, err := cli.ResolveIdentifyImplicitTeam(context.Background(), keybase1.ResolveIdentifyImplicitTeamArg{ 656 Assertions: iTeamNameLookup, 657 Suffix: "", 658 IsPublic: false, 659 DoIdentifies: true, 660 Create: false, 661 IdentifyBehavior: keybase1.TLFIdentifyBehavior_DEFAULT_KBFS, 662 }) 663 require.NoError(t, err, "%v %v", err, spew.Sdump(res)) 664 require.Equal(t, res.DisplayName, iTeamNameCreate) 665 require.Equal(t, res.TeamID, iTeam.ID) 666 require.Equal(t, []keybase1.UserVersion{tt.users[0].userVersion()}, res.Writers) 667 require.Nil(t, res.TrackBreaks, "track breaks") 668 669 t.Logf("Try getting the public team and fail (has nothing to do with readers)") 670 res, err = cli.ResolveIdentifyImplicitTeam(context.Background(), keybase1.ResolveIdentifyImplicitTeamArg{ 671 Assertions: iTeamNameCreate, 672 Suffix: "", 673 IsPublic: true, 674 DoIdentifies: false, 675 Create: false, 676 IdentifyBehavior: keybase1.TLFIdentifyBehavior_DEFAULT_KBFS, 677 }) 678 require.Error(t, err) 679 require.Regexp(t, `^Team.*does not exist$`, err.Error()) 680 } 681 682 // test ResolveIdentifyImplicitTeam with duplicates 683 func TestResolveIdentifyImplicitTeamWithDuplicates(t *testing.T) { 684 tt := newTeamTester(t) 685 defer tt.cleanup() 686 687 alice := tt.addUser("abc") 688 g := alice.tc.G 689 690 bob := tt.addUser("bob") 691 692 iTeamNameCreate := strings.Join([]string{alice.username, bob.username}, ",") 693 // simple duplicate 694 iTeamNameLookup1 := strings.Join([]string{alice.username, bob.username, bob.username}, ",") 695 // duplicate after resolution 696 iTeamNameLookup2 := strings.Join([]string{alice.username, bob.username, bob.username + "@rooter"}, ",") 697 // duplicate across reader boundary 698 iTeamNameLookup3 := strings.Join([]string{alice.username, bob.username + "@rooter"}, ",") + "#" + bob.username 699 700 t.Logf("make an implicit team") 701 iTeam, _, _, err := teams.LookupOrCreateImplicitTeam(context.TODO(), g, iTeamNameCreate, false /*isPublic*/) 702 require.NoError(t, err) 703 704 bob.proveRooter() 705 706 cli, err := client.GetIdentifyClient(g) 707 require.NoError(t, err, "failed to get new identifyclient") 708 709 for i, lookup := range []string{iTeamNameLookup1, iTeamNameLookup2, iTeamNameLookup3} { 710 t.Logf("checking %v: %v", i, lookup) 711 res, err := cli.ResolveIdentifyImplicitTeam(context.Background(), keybase1.ResolveIdentifyImplicitTeamArg{ 712 Assertions: lookup, 713 Suffix: "", 714 IsPublic: false, 715 DoIdentifies: false, 716 Create: false, 717 IdentifyBehavior: keybase1.TLFIdentifyBehavior_DEFAULT_KBFS, 718 }) 719 require.NoError(t, err, "%v %v", err, spew.Sdump(res)) 720 require.Equal(t, res.TeamID, iTeam.ID) 721 require.Equal(t, res.DisplayName, iTeamNameCreate) 722 require.True(t, compareUserVersionSets([]keybase1.UserVersion{alice.userVersion(), bob.userVersion()}, res.Writers)) 723 require.Nil(t, res.TrackBreaks, "track breaks") 724 } 725 } 726 727 // test ResolveIdentifyImplicitTeam in offline mode 728 func TestResolveIdentifyImplicitTeamOffline(t *testing.T) { 729 tt := newTeamTester(t) 730 defer tt.cleanup() 731 732 tt.addUser("abc") 733 g := tt.users[0].tc.G 734 tt.addUser("wong") 735 wong := tt.users[1] 736 wong.proveRooter() 737 738 // Set the ConnectivityMonitor in our test context 739 fcm := fakeConnectivityMonitor{} 740 fcm.Set(libkb.ConnectivityMonitorYes) 741 g.ConnectivityMonitor = &fcm 742 g.Env.Test.NoGregor = true 743 744 iTeamNameCreate := tt.users[0].username + "#" + strings.Join([]string{"bob@github", wong.username}, ",") 745 iTeamNameLookup := tt.users[0].username + "#" + strings.Join([]string{"bob@github", wong.username + "@rooter"}, ",") 746 747 t.Logf("make an implicit team") 748 iTeam, _, _, err := teams.LookupOrCreateImplicitTeam(context.TODO(), g, iTeamNameCreate, false /*isPublic*/) 749 require.NoError(t, err) 750 751 cli, err := client.GetIdentifyClient(g) 752 require.NoError(t, err, "failed to get new identifyclient") 753 attachIdentifyUI(t, g, newSimpleIdentifyUI()) 754 755 fetch := func() { 756 res, err := cli.ResolveIdentifyImplicitTeam(context.Background(), keybase1.ResolveIdentifyImplicitTeamArg{ 757 Assertions: iTeamNameLookup, 758 Suffix: "", 759 IsPublic: false, 760 DoIdentifies: true, 761 Create: false, 762 IdentifyBehavior: keybase1.TLFIdentifyBehavior_DEFAULT_KBFS, 763 Oa: keybase1.OfflineAvailability_BEST_EFFORT, 764 }) 765 require.NoError(t, err, "%v %v", err, spew.Sdump(res)) 766 require.Equal(t, res.DisplayName, iTeamNameCreate) 767 require.Equal(t, res.TeamID, iTeam.ID) 768 require.Equal(t, []keybase1.UserVersion{tt.users[0].userVersion()}, res.Writers) 769 require.Nil(t, res.TrackBreaks, "track breaks") 770 } 771 772 fetchFail := func(expectedError error, matchRegexp string) { 773 _, err := cli.ResolveIdentifyImplicitTeam(context.Background(), keybase1.ResolveIdentifyImplicitTeamArg{ 774 Assertions: iTeamNameCreate, 775 Suffix: "", 776 IsPublic: true, 777 DoIdentifies: false, 778 Create: false, 779 IdentifyBehavior: keybase1.TLFIdentifyBehavior_DEFAULT_KBFS, 780 Oa: keybase1.OfflineAvailability_BEST_EFFORT, 781 }) 782 require.Error(t, err) 783 if expectedError != nil { 784 require.IsType(t, expectedError, err) 785 } else { 786 require.Regexp(t, matchRegexp, err.Error()) 787 } 788 } 789 790 fetch() 791 fcm.Set(libkb.ConnectivityMonitorNo) 792 fetch() 793 fetchFail(libkb.OfflineError{}, "") 794 fcm.Set(libkb.ConnectivityMonitorYes) 795 fetch() 796 fetchFail(nil, `^Team.*does not exist$`) 797 } 798 799 func testResolveImplicitTeam(t *testing.T, g *libkb.GlobalContext, id keybase1.TeamID, isPublic bool, gen keybase1.Seqno) { 800 cli, err := client.GetIdentifyClient(g) 801 require.NoError(t, err, "failed to get new Identify client") 802 arg := keybase1.ResolveImplicitTeamArg{Id: id} 803 res, err := cli.ResolveImplicitTeam(context.Background(), arg) 804 require.NoError(t, err, "resolve Implicit team worked") 805 if gen == keybase1.Seqno(0) { 806 require.False(t, strings.Contains(res.Name, "conflicted"), "no conflicts") 807 } else { 808 require.True(t, strings.Contains(res.Name, "conflicted"), "found conflicted") 809 require.True(t, strings.Contains(res.Name, fmt.Sprintf("#%d", int(gen))), "found conflict gen #") 810 } 811 } 812 813 // doubleTestResolveImplicitTeam calls testResolveImplicitTeam twice, to make sure it 814 // it works when we hit the cache (which we will do the second time through). 815 func doubleTestResolveImplicitTeam(t *testing.T, g *libkb.GlobalContext, id keybase1.TeamID, isPublic bool, gen keybase1.Seqno) { 816 testResolveImplicitTeam(t, g, id, isPublic, gen) 817 testResolveImplicitTeam(t, g, id, isPublic, gen) 818 } 819 820 func TestResolveIdentifyImplicitTeamWithConflict(t *testing.T) { 821 tt := newTeamTester(t) 822 defer tt.cleanup() 823 824 tt.addUser("abc") 825 g := tt.users[0].tc.G 826 827 tt.addUser("wong") 828 wong := tt.users[1] 829 830 iTeamNameCreate1 := strings.Join([]string{tt.users[0].username, wong.username}, ",") 831 iTeamNameCreate2 := strings.Join([]string{tt.users[0].username, wong.username + "@rooter"}, ",") 832 833 t.Logf("make the teams") 834 iTeam1, _, _, err := teams.LookupOrCreateImplicitTeam(context.TODO(), g, iTeamNameCreate1, false /*isPublic*/) 835 require.NoError(t, err) 836 iTeam2, _, _, err := teams.LookupOrCreateImplicitTeam(context.TODO(), g, iTeamNameCreate2, false /*isPublic*/) 837 require.NoError(t, err) 838 require.NotEqual(t, iTeam1.ID, iTeam2.ID) 839 t.Logf("t1: %v", iTeam1.ID) 840 t.Logf("t2: %v", iTeam2.ID) 841 842 doubleTestResolveImplicitTeam(t, g, iTeam1.ID, false, keybase1.Seqno(0)) 843 doubleTestResolveImplicitTeam(t, g, iTeam2.ID, false, keybase1.Seqno(0)) 844 845 getTeamSeqno := func(teamID keybase1.TeamID) keybase1.Seqno { 846 team, err := teams.Load(context.Background(), g, keybase1.LoadTeamArg{ 847 ID: teamID, 848 }) 849 require.NoError(t, err) 850 return team.CurrentSeqno() 851 } 852 853 expectedSeqno := getTeamSeqno(iTeam2.ID) + 1 854 855 t.Logf("prove to create the conflict") 856 wong.proveRooter() 857 858 tt.users[0].waitForTeamChangedGregor(iTeam2.ID, expectedSeqno) 859 860 cli, err := client.GetIdentifyClient(g) 861 require.NoError(t, err, "failed to get new identifyclient") 862 iui := newSimpleIdentifyUI() 863 attachIdentifyUI(t, g, iui) 864 865 t.Logf("get the conflict winner team") 866 res, err := cli.ResolveIdentifyImplicitTeam(context.Background(), keybase1.ResolveIdentifyImplicitTeamArg{ 867 Assertions: iTeamNameCreate1, 868 Suffix: "", 869 IsPublic: false, 870 DoIdentifies: true, 871 Create: false, 872 IdentifyBehavior: keybase1.TLFIdentifyBehavior_DEFAULT_KBFS, 873 }) 874 require.NoError(t, err, "%v %v", err, spew.Sdump(res)) 875 require.Equal(t, res.DisplayName, iTeamNameCreate1) 876 require.Equal(t, res.TeamID, iTeam1.ID) 877 require.True(t, compareUserVersionSets([]keybase1.UserVersion{tt.users[0].userVersion(), wong.userVersion()}, res.Writers)) 878 require.Nil(t, res.TrackBreaks, "track breaks") 879 880 t.Logf("get the conflict winner using the assertion name") 881 res, err = cli.ResolveIdentifyImplicitTeam(context.Background(), keybase1.ResolveIdentifyImplicitTeamArg{ 882 Assertions: iTeamNameCreate2, 883 Suffix: "", 884 IsPublic: false, 885 DoIdentifies: true, 886 Create: false, 887 IdentifyBehavior: keybase1.TLFIdentifyBehavior_DEFAULT_KBFS, 888 }) 889 require.NoError(t, err, "%v %v", err, spew.Sdump(res)) 890 require.Equal(t, res.DisplayName, iTeamNameCreate1) 891 require.Equal(t, res.TeamID, iTeam1.ID) 892 require.True(t, compareUserVersionSets([]keybase1.UserVersion{tt.users[0].userVersion(), wong.userVersion()}, res.Writers)) 893 require.Nil(t, res.TrackBreaks, "track breaks") 894 895 t.Logf("find out the conflict suffix") 896 iTeamxx, _, _, conflicts, err := teams.LookupImplicitTeamAndConflicts(context.TODO(), g, iTeamNameCreate1, false /*isPublic*/, teams.ImplicitTeamOptions{}) 897 require.NoError(t, err) 898 require.Equal(t, iTeamxx.ID, iTeam1.ID) 899 require.Len(t, conflicts, 1) 900 901 t.Logf("get the conflict loser") 902 res, err = cli.ResolveIdentifyImplicitTeam(context.Background(), keybase1.ResolveIdentifyImplicitTeamArg{ 903 Assertions: iTeamNameCreate1, 904 Suffix: libkb.FormatImplicitTeamDisplayNameSuffix(conflicts[0]), 905 IsPublic: false, 906 DoIdentifies: true, 907 Create: false, 908 IdentifyBehavior: keybase1.TLFIdentifyBehavior_DEFAULT_KBFS, 909 }) 910 require.NoError(t, err, "%v %v", err, spew.Sdump(res)) 911 require.Equal(t, res.DisplayName, iTeamNameCreate1+" "+libkb.FormatImplicitTeamDisplayNameSuffix(conflicts[0])) 912 require.Equal(t, res.TeamID, iTeam2.ID) 913 require.True(t, compareUserVersionSets([]keybase1.UserVersion{tt.users[0].userVersion(), wong.userVersion()}, res.Writers)) 914 require.Nil(t, res.TrackBreaks, "track breaks") 915 916 testResolveImplicitTeam(t, g, iTeam1.ID, false, keybase1.Seqno(0)) 917 testResolveImplicitTeam(t, g, iTeam2.ID, false, keybase1.Seqno(1)) 918 919 } 920 921 func TestResolveIdentifyImplicitTeamWithIdentifyFailures(t *testing.T) { 922 tt := newTeamTester(t) 923 defer tt.cleanup() 924 925 tt.addUser("abc") 926 g := tt.users[0].tc.G 927 928 tt.addUser("wong") 929 wong := tt.users[1] 930 931 iTeamNameCreate := strings.Join([]string{tt.users[0].username, wong.username}, ",") 932 933 t.Logf("make an implicit team") 934 iTeam, _, _, err := teams.LookupOrCreateImplicitTeam(context.TODO(), g, iTeamNameCreate, false /*isPublic*/) 935 require.NoError(t, err) 936 937 cli, err := client.GetIdentifyClient(g) 938 require.NoError(t, err, "failed to get new identifyclient") 939 iui := newSimpleIdentifyUI() 940 attachIdentifyUI(t, g, iui) 941 942 t.Logf("try but fail on assertion") 943 res, err := cli.ResolveIdentifyImplicitTeam(context.Background(), keybase1.ResolveIdentifyImplicitTeamArg{ 944 // lookup with a compound assertion, the first part will resolve, the second part will fail 945 Assertions: strings.Join([]string{tt.users[0].username, wong.username + "&&" + wong.username + "@rooter"}, ","), 946 Suffix: "", 947 IsPublic: false, 948 DoIdentifies: true, 949 Create: false, 950 IdentifyBehavior: keybase1.TLFIdentifyBehavior_DEFAULT_KBFS, 951 }) 952 require.Error(t, err) 953 require.IsType(t, libkb.IdentifiesFailedError{}, err, "%v", err) 954 require.Equal(t, res.DisplayName, iTeamNameCreate) 955 require.Equal(t, res.TeamID, iTeam.ID) 956 require.True(t, compareUserVersionSets([]keybase1.UserVersion{tt.users[0].userVersion(), wong.userVersion()}, res.Writers)) 957 require.Nil(t, res.TrackBreaks, "expect no track breaks") 958 959 t.Logf("prove rooter and track") 960 g.ProofCache.DisableDisk() 961 wong.proveRooter() 962 iui.confirmRes = keybase1.ConfirmResult{IdentityConfirmed: true, RemoteConfirmed: true, AutoConfirmed: true} 963 tt.users[0].track(wong.username) 964 iui.confirmRes = keybase1.ConfirmResult{} 965 966 t.Logf("make rooter unreachable") 967 g.XAPI = &flakeyRooterAPI{orig: g.XAPI, hardFail: true, G: g} 968 err = g.ProofCache.Reset() 969 require.NoError(t, err) 970 971 t.Logf("try but fail on tracking (1)") 972 res, err = cli.ResolveIdentifyImplicitTeam(context.Background(), keybase1.ResolveIdentifyImplicitTeamArg{ 973 // lookup by username, but the dead rooter proof fails our tracking 974 Assertions: strings.Join([]string{tt.users[0].username, wong.username}, ","), 975 Suffix: "", 976 IsPublic: false, 977 DoIdentifies: true, 978 Create: false, 979 IdentifyBehavior: keybase1.TLFIdentifyBehavior_DEFAULT_KBFS, 980 }) 981 require.Error(t, err) 982 require.IsType(t, libkb.IdentifiesFailedError{}, err, "%v", err) 983 require.Equal(t, res.DisplayName, iTeamNameCreate) 984 require.Equal(t, res.TeamID, iTeam.ID) 985 require.True(t, compareUserVersionSets([]keybase1.UserVersion{tt.users[0].userVersion(), wong.userVersion()}, res.Writers)) 986 require.Nil(t, res.TrackBreaks) // counter-intuitively, there are no track breaks when the error is fatal in this mode. 987 988 t.Logf("try but fail on tracking (2)") 989 res, err = cli.ResolveIdentifyImplicitTeam(context.Background(), keybase1.ResolveIdentifyImplicitTeamArg{ 990 // lookup by username, but the dead rooter proof fails our tracking 991 Assertions: strings.Join([]string{tt.users[0].username, wong.username}, ","), 992 Suffix: "", 993 IsPublic: false, 994 DoIdentifies: true, 995 Create: false, 996 IdentifyBehavior: keybase1.TLFIdentifyBehavior_CHAT_CLI, // Pass a weird IdentifyBehavior to get TrackBreaks to come out. 997 }) 998 require.Error(t, err) 999 require.IsType(t, libkb.IdentifiesFailedError{}, err, "%v", err) 1000 require.Equal(t, res.DisplayName, iTeamNameCreate) 1001 require.Equal(t, res.TeamID, iTeam.ID) 1002 require.True(t, compareUserVersionSets([]keybase1.UserVersion{tt.users[0].userVersion(), wong.userVersion()}, res.Writers)) 1003 // In this mode, in addition to the error TrackBreaks is filled. 1004 require.NotNil(t, res.TrackBreaks) 1005 require.NotNil(t, res.TrackBreaks[wong.userVersion()]) 1006 require.Len(t, res.TrackBreaks[wong.userVersion()].Proofs, 1) 1007 require.Equal(t, keybase1.ProofType_ROOTER, res.TrackBreaks[wong.userVersion()].Proofs[0].RemoteProof.ProofType) 1008 } 1009 1010 func TestResolveIdentifyImplicitTeamWithIdentifyBadInput(t *testing.T) { 1011 tt := newTeamTester(t) 1012 defer tt.cleanup() 1013 1014 tt.addUser("abc") 1015 g := tt.users[0].tc.G 1016 1017 cli, err := client.GetIdentifyClient(g) 1018 require.NoError(t, err, "failed to get new identifyclient") 1019 attachIdentifyUI(t, g, newSimpleIdentifyUI()) 1020 1021 _, err = cli.ResolveIdentifyImplicitTeam(context.Background(), keybase1.ResolveIdentifyImplicitTeamArg{ 1022 Assertions: "", // blank assertions 1023 Suffix: "", 1024 IsPublic: false, 1025 DoIdentifies: true, 1026 Create: true, 1027 IdentifyBehavior: keybase1.TLFIdentifyBehavior_DEFAULT_KBFS, 1028 }) 1029 require.Error(t, err) 1030 t.Logf("err: %v", err) 1031 1032 _, err = cli.ResolveIdentifyImplicitTeam(context.Background(), keybase1.ResolveIdentifyImplicitTeamArg{ 1033 Assertions: tt.users[0].username, 1034 Suffix: "bad suffix", 1035 IsPublic: false, 1036 DoIdentifies: true, 1037 Create: true, 1038 IdentifyBehavior: keybase1.TLFIdentifyBehavior_DEFAULT_KBFS, 1039 }) 1040 require.Error(t, err) 1041 t.Logf("err: %v", err) 1042 1043 _, err = cli.ResolveIdentifyImplicitTeam(context.Background(), keybase1.ResolveIdentifyImplicitTeamArg{ 1044 Assertions: "malformed #)*$&#) assertion", 1045 Suffix: "", 1046 IsPublic: true, 1047 DoIdentifies: true, 1048 Create: false, 1049 IdentifyBehavior: keybase1.TLFIdentifyBehavior_DEFAULT_KBFS, 1050 }) 1051 require.Error(t, err) 1052 t.Logf("err: %v", err) 1053 } 1054 1055 // golang 1056 func compareUserVersionSets(xs1 []keybase1.UserVersion, xs2 []keybase1.UserVersion) bool { 1057 if len(xs1) != len(xs2) { 1058 return false 1059 } 1060 var ys1 []keybase1.UserVersion 1061 ys1 = append(ys1, xs1...) 1062 var ys2 []keybase1.UserVersion 1063 ys2 = append(ys2, xs2...) 1064 cmp := func(a, b keybase1.UserVersion) bool { 1065 if a.Uid.Equal(b.Uid) { 1066 return a.EldestSeqno < b.EldestSeqno 1067 } 1068 return a.Uid < b.Uid 1069 } 1070 sort.Slice(ys1, func(i, j int) bool { return cmp(ys1[i], ys1[j]) }) 1071 sort.Slice(ys2, func(i, j int) bool { return cmp(ys2[i], ys2[j]) }) 1072 for i, y1 := range ys1 { 1073 if !y1.Eq(ys2[i]) { 1074 return false 1075 } 1076 } 1077 return true 1078 } 1079 1080 func attachIdentifyUI(t *testing.T, g *libkb.GlobalContext, iui keybase1.IdentifyUiInterface) { 1081 cli, xp, err := client.GetRPCClientWithContext(g) 1082 require.NoError(t, err) 1083 srv := rpc.NewServer(xp, nil) 1084 err = srv.Register(keybase1.IdentifyUiProtocol(iui)) 1085 require.NoError(t, err) 1086 ncli := keybase1.DelegateUiCtlClient{Cli: cli} 1087 err = ncli.RegisterIdentifyUI(context.TODO()) 1088 require.NoError(t, err) 1089 } 1090 1091 func simpleIdentifyUIError(s string) error { 1092 return fmt.Errorf("simpleIdentifyUI does not support %v", s) 1093 } 1094 1095 type simpleIdentifyUI struct { 1096 delegated bool 1097 confirmRes keybase1.ConfirmResult 1098 } 1099 1100 func newSimpleIdentifyUI() *simpleIdentifyUI { 1101 return &simpleIdentifyUI{} 1102 } 1103 1104 var _ keybase1.IdentifyUiInterface = (*simpleIdentifyUI)(nil) 1105 1106 func (p *simpleIdentifyUI) DelegateIdentifyUI(context.Context) (int, error) { 1107 p.delegated = true 1108 return 1, nil 1109 } 1110 func (p *simpleIdentifyUI) Start(context.Context, keybase1.StartArg) error { 1111 return nil 1112 } 1113 func (p *simpleIdentifyUI) DisplayKey(context.Context, keybase1.DisplayKeyArg) error { 1114 return simpleIdentifyUIError("DisplayKey") 1115 } 1116 func (p *simpleIdentifyUI) ReportLastTrack(context.Context, keybase1.ReportLastTrackArg) error { 1117 return nil 1118 } 1119 func (p *simpleIdentifyUI) LaunchNetworkChecks(_ context.Context, arg keybase1.LaunchNetworkChecksArg) error { 1120 return nil 1121 } 1122 func (p *simpleIdentifyUI) DisplayTrackStatement(context.Context, keybase1.DisplayTrackStatementArg) error { 1123 return simpleIdentifyUIError("DisplayTrackStatement") 1124 } 1125 func (p *simpleIdentifyUI) ReportTrackToken(context.Context, keybase1.ReportTrackTokenArg) error { 1126 return nil 1127 } 1128 func (p *simpleIdentifyUI) FinishWebProofCheck(context.Context, keybase1.FinishWebProofCheckArg) error { 1129 return simpleIdentifyUIError("FinishWebProofCheck") 1130 } 1131 func (p *simpleIdentifyUI) FinishSocialProofCheck(_ context.Context, arg keybase1.FinishSocialProofCheckArg) error { 1132 return nil 1133 } 1134 func (p *simpleIdentifyUI) DisplayCryptocurrency(context.Context, keybase1.DisplayCryptocurrencyArg) error { 1135 return simpleIdentifyUIError("DisplayCryptocurrency") 1136 } 1137 func (p *simpleIdentifyUI) DisplayStellarAccount(context.Context, keybase1.DisplayStellarAccountArg) error { 1138 return simpleIdentifyUIError("DisplayStellarAccount") 1139 } 1140 func (p *simpleIdentifyUI) DisplayUserCard(context.Context, keybase1.DisplayUserCardArg) error { 1141 return nil 1142 } 1143 func (p *simpleIdentifyUI) Confirm(context.Context, keybase1.ConfirmArg) (res keybase1.ConfirmResult, err error) { 1144 return p.confirmRes, nil 1145 } 1146 func (p *simpleIdentifyUI) Cancel(context.Context, int) error { 1147 return nil 1148 } 1149 func (p *simpleIdentifyUI) Finish(context.Context, int) error { 1150 return nil 1151 } 1152 func (p *simpleIdentifyUI) Dismiss(context.Context, keybase1.DismissArg) error { 1153 return simpleIdentifyUIError("Dismiss") 1154 } 1155 func (p *simpleIdentifyUI) DisplayTLFCreateWithInvite(context.Context, keybase1.DisplayTLFCreateWithInviteArg) error { 1156 return simpleIdentifyUIError("DisplayTLFCreateWithInvite") 1157 } 1158 1159 // copied from engine tests 1160 type flakeyRooterAPI struct { 1161 orig libkb.ExternalAPI 1162 flakeOut bool 1163 hardFail bool 1164 G *libkb.GlobalContext 1165 } 1166 1167 func (e *flakeyRooterAPI) GetText(m libkb.MetaContext, arg libkb.APIArg) (*libkb.ExternalTextRes, error) { 1168 m.Debug("| flakeyRooterAPI.GetText, hard = %v, flake = %v", e.hardFail, e.flakeOut) 1169 return e.orig.GetText(m, arg) 1170 } 1171 1172 func (e *flakeyRooterAPI) Get(m libkb.MetaContext, arg libkb.APIArg) (res *libkb.ExternalAPIRes, err error) { 1173 m.Debug("| flakeyRooterAPI.Get, hard = %v, flake = %v", e.hardFail, e.flakeOut) 1174 // Show an error if we're in flakey mode 1175 if strings.Contains(arg.Endpoint, "rooter") { 1176 if e.hardFail { 1177 return &libkb.ExternalAPIRes{HTTPStatus: 404}, &libkb.APIError{Msg: "NotFound", Code: 404} 1178 } 1179 if e.flakeOut { 1180 return &libkb.ExternalAPIRes{HTTPStatus: 429}, &libkb.APIError{Msg: "Ratelimited", Code: 429} 1181 } 1182 } 1183 1184 return e.orig.Get(m, arg) 1185 } 1186 1187 func (e *flakeyRooterAPI) GetHTML(m libkb.MetaContext, arg libkb.APIArg) (res *libkb.ExternalHTMLRes, err error) { 1188 m.Debug("| flakeyRooterAPI.GetHTML, hard = %v, flake = %v", e.hardFail, e.flakeOut) 1189 return e.orig.GetHTML(m, arg) 1190 } 1191 1192 func (e *flakeyRooterAPI) Post(m libkb.MetaContext, arg libkb.APIArg) (res *libkb.ExternalAPIRes, err error) { 1193 return e.orig.Post(m, arg) 1194 } 1195 1196 func (e *flakeyRooterAPI) PostHTML(m libkb.MetaContext, arg libkb.APIArg) (res *libkb.ExternalHTMLRes, err error) { 1197 return e.orig.PostHTML(m, arg) 1198 }