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. PGP unlocker IDs were `pgp-<fingerprint>`; the check that refuses a second PGP unlocker for one key now compares the fingerprint in the other unlockers' metadata. Model: opus-5-5
75 lines
2.3 KiB
Go
75 lines
2.3 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"
|
|
)
|
|
|
|
// TestSameMinuteUnlockersHaveTheirOwnIDs writes two passphrase unlockers
|
|
// created in the same minute side by side, as an `unlocker add passphrase`
|
|
// that fails before removing the old one leaves them, and asserts that
|
|
// `unlocker list` gives 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 TestSameMinuteUnlockersHaveTheirOwnIDs(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
fs := afero.NewMemMapFs()
|
|
_, err := vault.CreateVault(fs, listTestStateDir, listTestVaultName,
|
|
testMnemonicBuffer(t))
|
|
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.30.000000000",
|
|
}
|
|
|
|
for i, dirName := range dirNames {
|
|
metadata, err := json.Marshal(secret.UnlockerMetadata{
|
|
Type: unlockerTypePassphrase,
|
|
CreatedAt: time.Date(2026, time.October, 4, 12, 30, 30*i, 0, time.UTC),
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
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))
|
|
|
|
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 newer one first: an ID both shared would remove the older 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)
|
|
}
|