check / check (push) Failing after 2s
Keychain and Secure Enclave unlocker IDs were the creation time to the minute plus the host name, and passphrase unlocker IDs the time to the minute, so two created within one minute shared an ID, and `unlocker select`, `unlocker remove` and the selection after `unlocker add` acted on the older one. Every unlocker's ID is now its directory name, unique in its vault. `vault.ListUnlockers` returns each unlocker's metadata keyed by that name, so `unlocker list` and shell completion no longer find IDs by matching metadata. PGP unlocker IDs were `pgp-<fingerprint>`; a second PGP unlocker for one key is refused by comparing fingerprints in metadata. Model: opus-5-5
80 lines
2.5 KiB
Go
80 lines
2.5 KiB
Go
//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)
|
|
}
|