Files
netbird/client/mobile/profile_lifecycle_test.go
T
riccardom 54f1751026 [client] Say what each assertion was checking in the mobile test
AGENTS.md asks for a context message on comparison and boolean assertions,
and four of the ones added with this test had none, so a failure would have
read as a bare Empty/Equal with no hint of which step of the round trip broke.

Reported by cubic on the PR.
2026-09-22 12:06:35 +02:00

98 lines
4.2 KiB
Go

package mobile
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/netbirdio/netbird/client/internal/profilemanager"
)
// loadAsTheMobileSDKsDo replays what the iOS SDK does with a stored profile:
// read the config, serialize it, and load it back. Client.SetConfigFromJSON
// stores that document for tvOS, Auth.SetConfigFromJSON authenticates with it,
// and copyConfig round-trips a Config through the same pair to take an
// in-memory copy before applying the MDM overlay.
func loadAsTheMobileSDKsDo(t *testing.T, configPath string) *profilemanager.Config {
t.Helper()
stored, err := profilemanager.GetExistingConfig(configPath)
require.NoError(t, err, "read the stored profile")
document, err := profilemanager.ConfigToJSON(stored)
require.NoError(t, err, "serialize the stored profile")
reloaded, err := profilemanager.ConfigFromJSON(document)
require.NoError(t, err, "load the profile back")
return reloaded
}
// A profile survives the whole round its user puts it through: created, logged
// out, loaded again, and switched away from and back.
//
// Logout is the step that makes this worth asserting. It clears the peer's
// keys in place so the next login registers a new peer rather than bringing
// the old one back, which leaves a profile that legitimately carries no
// identity — and both mobile SDKs go on loading that profile through the
// serialized form. A load that refused it, or a creation that never wrote an
// identity in the first place, breaks logout and profile switching on iOS and
// Android without any of it being visible from the desktop client.
func TestProfileSurvivesLogoutAndReload(t *testing.T) {
pm := newTestProfileManager(t)
created, err := pm.AddProfile("work")
require.NoError(t, err)
require.NoError(t, pm.SwitchProfile(profilemanager.DefaultProfileName))
// Created: the profile carries the identity it will connect with.
require.NotEmpty(t, privateKeyOf(t, pm, created.ID), "a new profile was written with no identity")
configPath, err := pm.GetConfigPath(created.ID)
require.NoError(t, err)
before := loadAsTheMobileSDKsDo(t, configPath)
require.NotEmpty(t, before.PrivateKey)
managementURL := before.ManagementURL.String()
// Logged out: the identity is gone, on purpose.
require.NoError(t, pm.LogoutProfile(created.ID))
require.Empty(t, privateKeyOf(t, pm, created.ID), "logout left the peer's key behind")
// Loaded again: the profile is still readable, and loading it neither
// fails nor mints a key that nothing would write down.
after := loadAsTheMobileSDKsDo(t, configPath)
assert.Empty(t, after.PrivateKey, "loading a logged-out profile minted a key nothing will persist")
assert.Empty(t, after.SSHKey, "loading a logged-out profile minted an SSH key")
assert.Equal(t, managementURL, after.ManagementURL.String(), "the rest of the profile did not survive the logout")
// Switched away from and back: still the same profile, still loadable.
require.NoError(t, pm.SwitchProfile(created.ID))
require.NoError(t, pm.SwitchProfile(profilemanager.DefaultProfileName))
require.NoError(t, pm.SwitchProfile(created.ID))
active, err := pm.GetActiveProfile()
require.NoError(t, err)
assert.Equal(t, created.ID, active.ID, "the profile switched to is not the active one")
assert.Equal(t, managementURL, loadAsTheMobileSDKsDo(t, configPath).ManagementURL.String(),
"the profile did not survive the round of switches")
}
// The profile the SDKs fall back to gets the same treatment, since it is the
// one a mobile client without an explicit profile runs on.
func TestDefaultProfileSurvivesLogoutAndReload(t *testing.T) {
pm := newTestProfileManager(t)
require.NoError(t, pm.SwitchProfile(profilemanager.DefaultProfileName))
configPath, err := pm.GetConfigPath(profilemanager.DefaultProfileName)
require.NoError(t, err)
require.NotEmpty(t, loadAsTheMobileSDKsDo(t, configPath).PrivateKey)
require.NoError(t, pm.LogoutProfile(profilemanager.DefaultProfileName))
reloaded := loadAsTheMobileSDKsDo(t, configPath)
assert.Empty(t, reloaded.PrivateKey, "loading the logged-out default profile minted a key")
assert.NotNil(t, reloaded.ManagementURL, "the profile lost its management URL")
}