mirror of
https://github.com/netbirdio/netbird.git
synced 2026-09-16 19:59:07 +02:00
[client] Cover the mobile profile round trip: create, logout, reload
Both mobile regressions this branch's review turned up lived on the same path, and neither was visible from the desktop client: a profile created without an identity, and a logged-out profile that would no longer deserialize. The desktop never meets the second one — it is mobile logout that clears the peer's keys in place, so the next login registers a new peer instead of bringing the old one back. The test walks a profile through the round its user puts it through — created, logged out, loaded again, switched away from and back — and loads it at each step the way the SDKs do: read the stored config, serialize it, load it back. That is Client.SetConfigFromJSON storing the document for tvOS, Auth.SetConfigFromJSON authenticating with it, and copyConfig taking an in-memory copy before the MDM overlay. Verified to fail on each regression separately: restoring the bare constructor in AddProfile fails it with "a new profile was written with no identity", and restoring the identity check in ConfigFromJSON fails it at "load the profile back". client/mobile already had the coverage for the first one in TestLogoutProfile_DisableProfiles — which arrived from main with the MDM work, and which I had not been running.
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
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)
|
||||
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)
|
||||
|
||||
assert.Equal(t, managementURL, loadAsTheMobileSDKsDo(t, configPath).ManagementURL.String())
|
||||
}
|
||||
|
||||
// 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)
|
||||
assert.NotNil(t, reloaded.ManagementURL, "the profile lost its management URL")
|
||||
}
|
||||
Reference in New Issue
Block a user