mirror of
https://github.com/tailscale/tailscale.git
synced 2025-06-08 00:38:34 +00:00

Ultimately, we'd like to get rid of the concept of the "current user". It is only used on Windows, but even then it doesn't work well in multi-user and enterprise/managed Windows environments. In this PR, we update LocalBackend and profileManager to decouple them a bit more from this obsolete concept. This is done in a preparation for extracting ipnlocal.Extension-related interfaces and types, and using them to implement optional features like tailscale/corp#27645, instead of continuing growing the core ipnlocal logic. Notably, we rename (*profileManager).SetCurrentUserAndProfile() to SwitchToProfile() and change its signature to accept an ipn.LoginProfileView instead of an ipn.ProfileID and ipn.WindowsUserID. Since we're not removing the "current user" completely just yet, the method sets the current user to the owner of the target profile. We also update the profileResolver callback type, which is typically implemented by LocalBackend extensions, to return an ipn.LoginProfileView instead of ipn.ProfileID and ipn.WindowsUserID. Updates tailscale/corp#27645 Updates tailscale/corp#18342 Signed-off-by: Nick Khyl <nickk@tailscale.com>
612 lines
16 KiB
Go
612 lines
16 KiB
Go
// Copyright (c) Tailscale Inc & AUTHORS
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
package ipnlocal
|
|
|
|
import (
|
|
"fmt"
|
|
"os/user"
|
|
"strconv"
|
|
"testing"
|
|
|
|
"github.com/google/go-cmp/cmp"
|
|
"github.com/google/go-cmp/cmp/cmpopts"
|
|
"tailscale.com/clientupdate"
|
|
"tailscale.com/health"
|
|
"tailscale.com/ipn"
|
|
"tailscale.com/ipn/store/mem"
|
|
"tailscale.com/tailcfg"
|
|
"tailscale.com/types/key"
|
|
"tailscale.com/types/logger"
|
|
"tailscale.com/types/persist"
|
|
"tailscale.com/util/must"
|
|
)
|
|
|
|
func TestProfileCurrentUserSwitch(t *testing.T) {
|
|
store := new(mem.Store)
|
|
|
|
pm, err := newProfileManagerWithGOOS(store, logger.Discard, new(health.Tracker), "linux")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
id := 0
|
|
newProfile := func(t *testing.T, loginName string) ipn.PrefsView {
|
|
id++
|
|
t.Helper()
|
|
pm.SwitchToNewProfile()
|
|
p := pm.CurrentPrefs().AsStruct()
|
|
p.Persist = &persist.Persist{
|
|
NodeID: tailcfg.StableNodeID(fmt.Sprint(id)),
|
|
PrivateNodeKey: key.NewNode(),
|
|
UserProfile: tailcfg.UserProfile{
|
|
ID: tailcfg.UserID(id),
|
|
LoginName: loginName,
|
|
},
|
|
}
|
|
if err := pm.SetPrefs(p.View(), ipn.NetworkProfile{}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return p.View()
|
|
}
|
|
|
|
pm.SetCurrentUserID("user1")
|
|
newProfile(t, "user1")
|
|
cp := pm.currentProfile
|
|
pm.DeleteProfile(cp.ID())
|
|
if !pm.currentProfile.Valid() {
|
|
t.Fatal("currentProfile is nil")
|
|
} else if pm.currentProfile.ID() != "" {
|
|
t.Fatalf("currentProfile.ID = %q, want empty", pm.currentProfile.ID())
|
|
}
|
|
if !pm.CurrentPrefs().Equals(defaultPrefs) {
|
|
t.Fatalf("CurrentPrefs() = %v, want emptyPrefs", pm.CurrentPrefs().Pretty())
|
|
}
|
|
|
|
pm, err = newProfileManagerWithGOOS(store, logger.Discard, new(health.Tracker), "linux")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
pm.SetCurrentUserID("user1")
|
|
if !pm.currentProfile.Valid() {
|
|
t.Fatal("currentProfile is nil")
|
|
} else if pm.currentProfile.ID() != "" {
|
|
t.Fatalf("currentProfile.ID = %q, want empty", pm.currentProfile.ID())
|
|
}
|
|
if !pm.CurrentPrefs().Equals(defaultPrefs) {
|
|
t.Fatalf("CurrentPrefs() = %v, want emptyPrefs", pm.CurrentPrefs().Pretty())
|
|
}
|
|
}
|
|
|
|
func TestProfileList(t *testing.T) {
|
|
store := new(mem.Store)
|
|
|
|
pm, err := newProfileManagerWithGOOS(store, logger.Discard, new(health.Tracker), "linux")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
id := 0
|
|
newProfile := func(t *testing.T, loginName string) ipn.PrefsView {
|
|
id++
|
|
t.Helper()
|
|
pm.SwitchToNewProfile()
|
|
p := pm.CurrentPrefs().AsStruct()
|
|
p.Persist = &persist.Persist{
|
|
NodeID: tailcfg.StableNodeID(fmt.Sprint(id)),
|
|
PrivateNodeKey: key.NewNode(),
|
|
UserProfile: tailcfg.UserProfile{
|
|
ID: tailcfg.UserID(id),
|
|
LoginName: loginName,
|
|
},
|
|
}
|
|
if err := pm.SetPrefs(p.View(), ipn.NetworkProfile{}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return p.View()
|
|
}
|
|
checkProfiles := func(t *testing.T, want ...string) {
|
|
t.Helper()
|
|
got := pm.Profiles()
|
|
if len(got) != len(want) {
|
|
t.Fatalf("got %d profiles, want %d", len(got), len(want))
|
|
}
|
|
for i, w := range want {
|
|
if got[i].Name() != w {
|
|
t.Errorf("got profile %d name %q, want %q", i, got[i].Name(), w)
|
|
}
|
|
}
|
|
}
|
|
|
|
pm.SetCurrentUserID("user1")
|
|
newProfile(t, "alice")
|
|
newProfile(t, "bob")
|
|
checkProfiles(t, "alice", "bob")
|
|
|
|
pm.SetCurrentUserID("user2")
|
|
checkProfiles(t)
|
|
newProfile(t, "carol")
|
|
carol := pm.currentProfile
|
|
checkProfiles(t, "carol")
|
|
|
|
pm.SetCurrentUserID("user1")
|
|
checkProfiles(t, "alice", "bob")
|
|
if lp := pm.findProfileByKey("user1", carol.Key()); lp.Valid() {
|
|
t.Fatalf("found profile for user2 in user1's profile list")
|
|
}
|
|
if lp := pm.findProfileByName("user1", carol.Name()); lp.Valid() {
|
|
t.Fatalf("found profile for user2 in user1's profile list")
|
|
}
|
|
|
|
pm.SetCurrentUserID("user2")
|
|
checkProfiles(t, "carol")
|
|
}
|
|
|
|
func TestProfileDupe(t *testing.T) {
|
|
newPersist := func(user, node int) *persist.Persist {
|
|
return &persist.Persist{
|
|
NodeID: tailcfg.StableNodeID(fmt.Sprintf("node%d", node)),
|
|
UserProfile: tailcfg.UserProfile{
|
|
ID: tailcfg.UserID(user),
|
|
LoginName: fmt.Sprintf("user%d@example.com", user),
|
|
},
|
|
}
|
|
}
|
|
user1Node1 := newPersist(1, 1)
|
|
user1Node2 := newPersist(1, 2)
|
|
user2Node1 := newPersist(2, 1)
|
|
user2Node2 := newPersist(2, 2)
|
|
user3Node3 := newPersist(3, 3)
|
|
|
|
reauth := func(pm *profileManager, p *persist.Persist) {
|
|
prefs := ipn.NewPrefs()
|
|
prefs.Persist = p
|
|
must.Do(pm.SetPrefs(prefs.View(), ipn.NetworkProfile{}))
|
|
}
|
|
login := func(pm *profileManager, p *persist.Persist) {
|
|
pm.SwitchToNewProfile()
|
|
reauth(pm, p)
|
|
}
|
|
|
|
type step struct {
|
|
fn func(pm *profileManager, p *persist.Persist)
|
|
p *persist.Persist
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
steps []step
|
|
profs []*persist.Persist
|
|
}{
|
|
{
|
|
name: "reauth-new-node",
|
|
steps: []step{
|
|
{login, user1Node1},
|
|
{reauth, user3Node3},
|
|
},
|
|
profs: []*persist.Persist{
|
|
user3Node3,
|
|
},
|
|
},
|
|
{
|
|
name: "reauth-same-node",
|
|
steps: []step{
|
|
{login, user1Node1},
|
|
{reauth, user1Node1},
|
|
},
|
|
profs: []*persist.Persist{
|
|
user1Node1,
|
|
},
|
|
},
|
|
{
|
|
name: "reauth-other-profile",
|
|
steps: []step{
|
|
{login, user1Node1},
|
|
{login, user2Node2},
|
|
{reauth, user1Node1},
|
|
},
|
|
profs: []*persist.Persist{
|
|
user1Node1,
|
|
user2Node2,
|
|
},
|
|
},
|
|
{
|
|
name: "reauth-replace-user",
|
|
steps: []step{
|
|
{login, user1Node1},
|
|
{login, user3Node3},
|
|
{reauth, user2Node1},
|
|
},
|
|
profs: []*persist.Persist{
|
|
user2Node1,
|
|
user3Node3,
|
|
},
|
|
},
|
|
{
|
|
name: "reauth-replace-node",
|
|
steps: []step{
|
|
{login, user1Node1},
|
|
{login, user3Node3},
|
|
{reauth, user1Node2},
|
|
},
|
|
profs: []*persist.Persist{
|
|
user1Node2,
|
|
user3Node3,
|
|
},
|
|
},
|
|
{
|
|
name: "login-same-node",
|
|
steps: []step{
|
|
{login, user1Node1},
|
|
{login, user3Node3}, // random other profile
|
|
{login, user1Node1},
|
|
},
|
|
profs: []*persist.Persist{
|
|
user1Node1,
|
|
user3Node3,
|
|
},
|
|
},
|
|
{
|
|
name: "login-replace-user",
|
|
steps: []step{
|
|
{login, user1Node1},
|
|
{login, user3Node3}, // random other profile
|
|
{login, user2Node1},
|
|
},
|
|
profs: []*persist.Persist{
|
|
user2Node1,
|
|
user3Node3,
|
|
},
|
|
},
|
|
{
|
|
name: "login-replace-node",
|
|
steps: []step{
|
|
{login, user1Node1},
|
|
{login, user3Node3}, // random other profile
|
|
{login, user1Node2},
|
|
},
|
|
profs: []*persist.Persist{
|
|
user1Node2,
|
|
user3Node3,
|
|
},
|
|
},
|
|
{
|
|
name: "login-new-node",
|
|
steps: []step{
|
|
{login, user1Node1},
|
|
{login, user2Node2},
|
|
},
|
|
profs: []*persist.Persist{
|
|
user1Node1,
|
|
user2Node2,
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
store := new(mem.Store)
|
|
pm, err := newProfileManagerWithGOOS(store, logger.Discard, new(health.Tracker), "linux")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, s := range tc.steps {
|
|
s.fn(pm, s.p)
|
|
}
|
|
profs := pm.Profiles()
|
|
var got []*persist.Persist
|
|
for _, p := range profs {
|
|
prefs, err := pm.loadSavedPrefs(p.Key())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
got = append(got, prefs.Persist().AsStruct())
|
|
}
|
|
d := cmp.Diff(tc.profs, got, cmpopts.SortSlices(func(a, b *persist.Persist) bool {
|
|
if a.NodeID != b.NodeID {
|
|
return a.NodeID < b.NodeID
|
|
}
|
|
return a.UserProfile.ID < b.UserProfile.ID
|
|
}))
|
|
if d != "" {
|
|
t.Fatal(d)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestProfileManagement tests creating, loading, and switching profiles.
|
|
func TestProfileManagement(t *testing.T) {
|
|
store := new(mem.Store)
|
|
|
|
pm, err := newProfileManagerWithGOOS(store, logger.Discard, new(health.Tracker), "linux")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
wantCurProfile := ""
|
|
wantProfiles := map[string]ipn.PrefsView{
|
|
"": defaultPrefs,
|
|
}
|
|
checkProfiles := func(t *testing.T) {
|
|
t.Helper()
|
|
prof := pm.CurrentProfile()
|
|
t.Logf("\tCurrentProfile = %q", prof.Name())
|
|
if prof.Name() != wantCurProfile {
|
|
t.Fatalf("CurrentProfile = %q; want %q", prof.Name(), wantCurProfile)
|
|
}
|
|
profiles := pm.Profiles()
|
|
wantLen := len(wantProfiles)
|
|
if _, ok := wantProfiles[""]; ok {
|
|
wantLen--
|
|
}
|
|
if len(profiles) != wantLen {
|
|
t.Fatalf("Profiles = %v; want %v", profiles, wantProfiles)
|
|
}
|
|
p := pm.CurrentPrefs()
|
|
t.Logf("\tCurrentPrefs = %s", p.Pretty())
|
|
if !p.Valid() {
|
|
t.Fatalf("CurrentPrefs = %v; want valid", p)
|
|
}
|
|
if !p.Equals(wantProfiles[wantCurProfile]) {
|
|
t.Fatalf("CurrentPrefs = %v; want %v", p.Pretty(), wantProfiles[wantCurProfile].Pretty())
|
|
}
|
|
for _, p := range profiles {
|
|
got, err := pm.loadSavedPrefs(p.Key())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// Use Hostname as a proxy for all prefs.
|
|
if !got.Equals(wantProfiles[p.Name()]) {
|
|
t.Fatalf("Prefs for profile %q =\n got=%+v\nwant=%v", p.Name(), got.Pretty(), wantProfiles[p.Name()].Pretty())
|
|
}
|
|
}
|
|
}
|
|
logins := make(map[string]tailcfg.UserID)
|
|
nodeIDs := make(map[string]tailcfg.StableNodeID)
|
|
setPrefs := func(t *testing.T, loginName string) ipn.PrefsView {
|
|
t.Helper()
|
|
p := pm.CurrentPrefs().AsStruct()
|
|
uid := logins[loginName]
|
|
if uid.IsZero() {
|
|
uid = tailcfg.UserID(len(logins) + 1)
|
|
logins[loginName] = uid
|
|
}
|
|
nid := nodeIDs[loginName]
|
|
if nid.IsZero() {
|
|
nid = tailcfg.StableNodeID(fmt.Sprint(len(nodeIDs) + 1))
|
|
nodeIDs[loginName] = nid
|
|
}
|
|
p.Persist = &persist.Persist{
|
|
PrivateNodeKey: key.NewNode(),
|
|
UserProfile: tailcfg.UserProfile{
|
|
ID: uid,
|
|
LoginName: loginName,
|
|
},
|
|
NodeID: nid,
|
|
}
|
|
if err := pm.SetPrefs(p.View(), ipn.NetworkProfile{}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return p.View()
|
|
}
|
|
t.Logf("Check initial state from empty store")
|
|
checkProfiles(t)
|
|
|
|
{
|
|
t.Logf("Set prefs for default profile")
|
|
wantProfiles["user@1.example.com"] = setPrefs(t, "user@1.example.com")
|
|
wantCurProfile = "user@1.example.com"
|
|
delete(wantProfiles, "")
|
|
}
|
|
checkProfiles(t)
|
|
|
|
t.Logf("Create new profile")
|
|
pm.SwitchToNewProfile()
|
|
wantCurProfile = ""
|
|
wantProfiles[""] = defaultPrefs
|
|
checkProfiles(t)
|
|
|
|
{
|
|
t.Logf("Set prefs for test profile")
|
|
wantProfiles["user@2.example.com"] = setPrefs(t, "user@2.example.com")
|
|
wantCurProfile = "user@2.example.com"
|
|
delete(wantProfiles, "")
|
|
}
|
|
checkProfiles(t)
|
|
|
|
t.Logf("Recreate profile manager from store")
|
|
// Recreate the profile manager to ensure that it can load the profiles
|
|
// from the store at startup.
|
|
pm, err = newProfileManagerWithGOOS(store, logger.Discard, new(health.Tracker), "linux")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
checkProfiles(t)
|
|
|
|
t.Logf("Delete default profile")
|
|
if err := pm.DeleteProfile(pm.ProfileIDForName("user@1.example.com")); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
delete(wantProfiles, "user@1.example.com")
|
|
checkProfiles(t)
|
|
|
|
t.Logf("Recreate profile manager from store after deleting default profile")
|
|
// Recreate the profile manager to ensure that it can load the profiles
|
|
// from the store at startup.
|
|
pm, err = newProfileManagerWithGOOS(store, logger.Discard, new(health.Tracker), "linux")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
checkProfiles(t)
|
|
|
|
t.Logf("Create new profile - 2")
|
|
pm.SwitchToNewProfile()
|
|
wantCurProfile = ""
|
|
wantProfiles[""] = defaultPrefs
|
|
checkProfiles(t)
|
|
|
|
t.Logf("Login with the existing profile")
|
|
wantProfiles["user@2.example.com"] = setPrefs(t, "user@2.example.com")
|
|
delete(wantProfiles, "")
|
|
wantCurProfile = "user@2.example.com"
|
|
checkProfiles(t)
|
|
|
|
t.Logf("Tag the current the profile")
|
|
nodeIDs["tagged-node.2.ts.net"] = nodeIDs["user@2.example.com"]
|
|
wantProfiles["tagged-node.2.ts.net"] = setPrefs(t, "tagged-node.2.ts.net")
|
|
delete(wantProfiles, "user@2.example.com")
|
|
wantCurProfile = "tagged-node.2.ts.net"
|
|
checkProfiles(t)
|
|
|
|
t.Logf("Relogin")
|
|
wantProfiles["user@2.example.com"] = setPrefs(t, "user@2.example.com")
|
|
delete(wantProfiles, "tagged-node.2.ts.net")
|
|
wantCurProfile = "user@2.example.com"
|
|
checkProfiles(t)
|
|
|
|
if !clientupdate.CanAutoUpdate() {
|
|
t.Logf("Save an invalid AutoUpdate pref value")
|
|
prefs := pm.CurrentPrefs().AsStruct()
|
|
prefs.AutoUpdate.Apply.Set(true)
|
|
if err := pm.SetPrefs(prefs.View(), ipn.NetworkProfile{}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !pm.CurrentPrefs().AutoUpdate().Apply.EqualBool(true) {
|
|
t.Fatal("SetPrefs failed to save auto-update setting")
|
|
}
|
|
// Re-load profiles to trigger migration for invalid auto-update value.
|
|
pm, err = newProfileManagerWithGOOS(store, logger.Discard, new(health.Tracker), "linux")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
checkProfiles(t)
|
|
if pm.CurrentPrefs().AutoUpdate().Apply.EqualBool(true) {
|
|
t.Fatal("invalid auto-update setting persisted after reload")
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestProfileManagementWindows tests going into and out of Unattended mode on
|
|
// Windows.
|
|
func TestProfileManagementWindows(t *testing.T) {
|
|
u, err := user.Current()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
uid := ipn.WindowsUserID(u.Uid)
|
|
|
|
store := new(mem.Store)
|
|
|
|
pm, err := newProfileManagerWithGOOS(store, logger.Discard, new(health.Tracker), "windows")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
wantCurProfile := ""
|
|
wantProfiles := map[string]ipn.PrefsView{
|
|
"": defaultPrefs,
|
|
}
|
|
checkProfiles := func(t *testing.T) {
|
|
t.Helper()
|
|
prof := pm.CurrentProfile()
|
|
t.Logf("\tCurrentProfile = %q", prof.Name())
|
|
if prof.Name() != wantCurProfile {
|
|
t.Fatalf("CurrentProfile = %q; want %q", prof.Name(), wantCurProfile)
|
|
}
|
|
if p := pm.CurrentPrefs(); !p.Equals(wantProfiles[wantCurProfile]) {
|
|
t.Fatalf("CurrentPrefs = %+v; want %+v", p.Pretty(), wantProfiles[wantCurProfile].Pretty())
|
|
}
|
|
}
|
|
logins := make(map[string]tailcfg.UserID)
|
|
setPrefs := func(t *testing.T, loginName string, forceDaemon bool) ipn.PrefsView {
|
|
id := logins[loginName]
|
|
if id.IsZero() {
|
|
id = tailcfg.UserID(len(logins) + 1)
|
|
logins[loginName] = id
|
|
}
|
|
p := pm.CurrentPrefs().AsStruct()
|
|
p.ForceDaemon = forceDaemon
|
|
p.Persist = &persist.Persist{
|
|
UserProfile: tailcfg.UserProfile{
|
|
ID: id,
|
|
LoginName: loginName,
|
|
},
|
|
NodeID: tailcfg.StableNodeID(strconv.Itoa(int(id))),
|
|
}
|
|
if err := pm.SetPrefs(p.View(), ipn.NetworkProfile{}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return p.View()
|
|
}
|
|
t.Logf("Check initial state from empty store")
|
|
checkProfiles(t)
|
|
|
|
{
|
|
t.Logf("Set user1 as logged in user")
|
|
pm.SetCurrentUserID(uid)
|
|
checkProfiles(t)
|
|
t.Logf("Save prefs for user1")
|
|
wantProfiles["default"] = setPrefs(t, "default", false)
|
|
wantCurProfile = "default"
|
|
}
|
|
checkProfiles(t)
|
|
|
|
{
|
|
t.Logf("Create new profile")
|
|
pm.SwitchToNewProfile()
|
|
wantCurProfile = ""
|
|
wantProfiles[""] = defaultPrefs
|
|
checkProfiles(t)
|
|
|
|
t.Logf("Save as test profile")
|
|
wantProfiles["test"] = setPrefs(t, "test", false)
|
|
wantCurProfile = "test"
|
|
checkProfiles(t)
|
|
}
|
|
|
|
t.Logf("Recreate profile manager from store, should reset prefs")
|
|
// Recreate the profile manager to ensure that it can load the profiles
|
|
// from the store at startup.
|
|
pm, err = newProfileManagerWithGOOS(store, logger.Discard, new(health.Tracker), "windows")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
wantCurProfile = ""
|
|
wantProfiles[""] = defaultPrefs
|
|
checkProfiles(t)
|
|
|
|
{
|
|
t.Logf("Set user1 as current user")
|
|
pm.SetCurrentUserID(uid)
|
|
wantCurProfile = "test"
|
|
}
|
|
checkProfiles(t)
|
|
{
|
|
t.Logf("set unattended mode")
|
|
wantProfiles["test"] = setPrefs(t, "test", true)
|
|
}
|
|
if pm.CurrentUserID() != uid {
|
|
t.Fatalf("CurrentUserID = %q; want %q", pm.CurrentUserID(), uid)
|
|
}
|
|
|
|
// Recreate the profile manager to ensure that it starts with test profile.
|
|
pm, err = newProfileManagerWithGOOS(store, logger.Discard, new(health.Tracker), "windows")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
checkProfiles(t)
|
|
if pm.CurrentUserID() != uid {
|
|
t.Fatalf("CurrentUserID = %q; want %q", pm.CurrentUserID(), uid)
|
|
}
|
|
}
|
|
|
|
// TestDefaultPrefs tests that defaultPrefs is just NewPrefs with
|
|
// LoggedOut=true (the Prefs we use before connecting to control). We shouldn't
|
|
// be putting any defaulting there, and instead put all defaults in NewPrefs.
|
|
func TestDefaultPrefs(t *testing.T) {
|
|
p1 := ipn.NewPrefs()
|
|
p1.LoggedOut = true
|
|
p1.WantRunning = false
|
|
p2 := defaultPrefs
|
|
if !p1.View().Equals(p2) {
|
|
t.Errorf("defaultPrefs is %s, want %s; defaultPrefs should only modify WantRunning and LoggedOut, all other defaults should be in ipn.NewPrefs.", p2.Pretty(), p1.Pretty())
|
|
}
|
|
}
|