OverviewFilesCommitsIssuesDocsReleasesPeers

hyperident commits / 65e9f0b

0.2.5: stop the test suite deleting the operator's real ~/.hyperident
`npm test` in this repo destroyed the vault of whoever ran it, on the FIRST test, every time. It did exactly that on 2026-08-17 (hyper-social #47), taking a live root identity that had no backup. Two helpers resolved the vault as path.join(os.homedir(), '.hyperident') and deleted it: test/identity-manager.test.js cleanupTestFiles() unlink every file + rmdir, called 81 times test/index.test.js cleanupTestIdentity() fs.rmSync recursive force FileManager already had setBasePath()/_customPath for exactly this purpose. The suite never called it. Two defences, because an env var alone fails OPEN if someone forgets it: 1. REDIRECT — lib/file-ops.js honours HYPERIDENT_HOME, so a test run or CI job can be isolated with no code change. Both suites now mkdtemp a vault and point setBasePath()/HYPERIDENT_HOME at it for the whole run. 2. REFUSE — both cleanup helpers throw if the resolved path IS the real vault, so a future edit that drops the redirect fails loudly instead of deleting an identity. Verified live: with the guard present and the redirect missing, index.test.js aborted rather than touching ~/.hyperident. Proof, run with a REAL vault on disk: before identity.pub 33480a56f618caa2...367559ff after identity.pub 33480a56f618caa2...367559ff byte-identical identity-manager.test.js 41/41 tests, 115/115 asserts test/index.test.js still fails at :858 ("Cannot set property stdin of #<process> which has only a getter") — PRE-EXISTING and unrelated, confirmed by reproducing it on the stashed original tree. Not fixed here. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
65e9f0b64ff8b4b33a95895ff0add4a5503a7192
by plankatron <fleeky@gmail.com> · 2026-08-17T23:11:31.000Z

Changed files (4) +40 -3

lib/file-ops.js +5 -0
……@@ -166,6 +166,11 @@ export class FileManager {
166166 */
167167 static getHyperidentPath() {
168168 if (FileManager._customPath) return FileManager._customPath
169 // HYPERIDENT_HOME redirects the vault WITHOUT a code change, so a test run or CI job can be
170 // isolated from the operator's real identity. This existed only as setBasePath(), which the test
171 // suite never called — so `npm test` deleted the real ~/.hyperident of whoever ran it. That cost a
172 // live vault on 2026-08-17 (hyper-social #47).
173 if (process.env.HYPERIDENT_HOME) return path.join(process.env.HYPERIDENT_HOME, '.hyperident')
169174 const homeDir = os.homedir()
170175 return path.join(homeDir, '.hyperident')
171176 }
package.json +1 -1
……@@ -1,6 +1,6 @@
11{
22 "name": "hyperident",
3 "version": "0.2.4",
3 "version": "0.2.5",
44 "main": "index.js",
55 "bin": {
66 "hyperident": "./index.js"
test/identity-manager.test.js +19 -1
……@@ -6,10 +6,28 @@ import fs from 'fs'
66import path from 'path'
77import os from 'os'
88
9// ISOLATION. This suite used to resolve the vault via os.homedir() and delete it — so `npm test`
10// destroyed the real ~/.hyperident of whoever ran it, on the FIRST test, every time. It did exactly
11// that on 2026-08-17 (hyper-social #47). Two defences, because an env var alone fails OPEN if someone
12// forgets it:
13// 1. redirect the vault into a temp dir for the whole run
14// 2. a hard guard that REFUSES to delete the real path even if 1 is missing
15const TEST_HOME = fs.mkdtempSync(path.join(os.tmpdir(), 'hyperident-test-'))
16process.env.HYPERIDENT_HOME = TEST_HOME
17FileManager.setBasePath(path.join(TEST_HOME, '.hyperident'))
18
19const REAL_VAULT = path.resolve(path.join(os.homedir(), '.hyperident'))
20function assertNotRealVault (p) {
21 if (path.resolve(p) === REAL_VAULT) {
22 throw new Error('REFUSING to delete the real vault at ' + p + ' — a test must redirect via FileManager.setBasePath()/HYPERIDENT_HOME first')
23 }
24}
25
926// Test helper to clean up test files
1027async function cleanupTestFiles() {
1128 try {
12 const hyperidentPath = path.join(os.homedir(), '.hyperident')
29 const hyperidentPath = FileManager.getHyperidentPath()
30 assertNotRealVault(hyperidentPath)
1331 if (fs.existsSync(hyperidentPath)) {
1432 // Remove all files in the directory
1533 const files = fs.readdirSync(hyperidentPath)
test/index.test.js +15 -1
……@@ -1,10 +1,18 @@
11import test from 'brittle'
22import { parseArgs, validateArgs, displayError, showHelp, showVersion, handleInit, handleCreateProfile, handleListProfiles, handleDeleteProfile, handleAddress } from '../index.js'
33import { IdentityManager } from '../lib/identity-manager.js'
4import { FileManager } from '../lib/file-ops.js'
45import fs from 'fs'
56import path from 'path'
67import os from 'os'
78
9// ISOLATION (hyper-social #47): this suite used to rmSync the operator's REAL ~/.hyperident. Redirect
10// the vault into a temp dir for the whole run; the guard in cleanupTestIdentity() is the second line
11// of defence if this is ever removed.
12const TEST_HOME = fs.mkdtempSync(path.join(os.tmpdir(), 'hyperident-idx-'))
13process.env.HYPERIDENT_HOME = TEST_HOME
14FileManager.setBasePath(path.join(TEST_HOME, '.hyperident'))
15
816// Mock console methods for testing output
917let consoleOutput = []
1018let consoleErrorOutput = []
……@@ -373,7 +381,13 @@ test('Complex argument parsing scenarios', (t) => {
373381// Helper function to clean up test identity
374382async function cleanupTestIdentity() {
375383 try {
376 const hyperidentPath = path.join(os.homedir(), '.hyperident')
384 // See identity-manager.test.js: this used to rmSync the operator's REAL vault (#47).
385 const hyperidentPath = process.env.HYPERIDENT_HOME
386 ? path.join(process.env.HYPERIDENT_HOME, '.hyperident')
387 : path.join(os.homedir(), '.hyperident')
388 if (path.resolve(hyperidentPath) === path.resolve(path.join(os.homedir(), '.hyperident'))) {
389 throw new Error('REFUSING to delete the real vault — set HYPERIDENT_HOME to a temp dir first')
390 }
377391 if (fs.existsSync(hyperidentPath)) {
378392 fs.rmSync(hyperidentPath, { recursive: true, force: true })
379393 }