//nolint:testpackage // white-box test of unexported internals package cli import ( "encoding/json" "path/filepath" "testing" "time" "git.eeqj.de/sneak/secret/internal/secret" "git.eeqj.de/sneak/secret/internal/vault" "github.com/spf13/afero" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) // TestSameMetadataUnlockersHaveTheirOwnIDs writes two passphrase unlockers // side by side whose metadata is the same, creation time included, as // copying an unlocker directory leaves them. It asserts that `unlocker // list` and the shell completion of `unlocker select` and `unlocker remove` // give each its own ID, and that each is selected and removed by its ID // alone. Keychain and Secure Enclave unlockers, which only macOS can add, // get their IDs the same way. func TestSameMetadataUnlockersHaveTheirOwnIDs(t *testing.T) { t.Parallel() fs := afero.NewMemMapFs() _, err := vault.CreateVault(fs, listTestStateDir, listTestVaultName, testMnemonicBuffer(t), nil) require.NoError(t, err) vaultDir := testVaultDir(listTestVaultName) unlockersDir := filepath.Join(vaultDir, listTestUnlockersDirName) dirNames := []string{ "passphrase-2026-10-04.12.30.00.000000000", "passphrase-2026-10-04.12.30.00.000000000-copy", } metadata, err := json.Marshal(secret.UnlockerMetadata{ Type: unlockerTypePassphrase, CreatedAt: time.Date(2026, time.October, 4, 12, 30, 0, 0, time.UTC), }) require.NoError(t, err) for _, dirName := range dirNames { dir := filepath.Join(unlockersDir, dirName) require.NoError(t, fs.MkdirAll(dir, listTestDirPerm)) require.NoError(t, afero.WriteFile(fs, filepath.Join(dir, listTestMetadataFileName), metadata, listTestFilePerm)) } listed := listUnlockersJSON(t, fs) require.Len(t, listed, len(dirNames)) completed, _ := getUnlockerIDsCompletionFunc(fs, listTestStateDir)( nil, nil, "") assert.Equal(t, dirNames, completed) instance, cmd := newTestInstance(fs) for i, unlocker := range listed { assert.Equal(t, dirNames[i], unlocker.ID) require.NoError(t, instance.UnlockerSelect(unlocker.ID)) current, err := afero.ReadFile(fs, filepath.Join(vaultDir, "current-unlocker")) require.NoError(t, err) assert.Equal(t, dirNames[i], string(current)) } // The second one first: an ID both shared would remove the first one require.NoError(t, instance.UnlockersRemove(listed[1].ID, true, cmd)) assertDirEntries(t, fs, unlockersDir, dirNames[0]) require.NoError(t, instance.UnlockersRemove(listed[0].ID, true, cmd)) assertDirEntries(t, fs, unlockersDir) }