WIP: A simple cli for daily tangled use cases and AI integration. This is for my personal use right now, but happy if others get mileage from it! :)
at main 150 lines 5.7 kB view raw
1import { execSync } from 'node:child_process'; 2import { beforeEach, describe, expect, it, vi } from 'vitest'; 3import { createSshKeyCommand } from '../../src/commands/ssh-key.js'; 4import * as sessionModule from '../../src/lib/session.js'; 5 6vi.mock('node:child_process'); 7vi.mock('../../src/lib/session.js'); 8 9describe('SSH Key Commands', () => { 10 let consoleLogSpy: ReturnType<typeof vi.fn>; 11 let consoleErrorSpy: ReturnType<typeof vi.fn>; 12 let processExitSpy: ReturnType<typeof vi.fn>; 13 14 beforeEach(() => { 15 vi.clearAllMocks(); 16 17 // Mock console methods 18 consoleLogSpy = vi.spyOn(console, 'log').mockImplementation(() => {}) as never; 19 consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {}) as never; 20 21 // Mock process.exit to throw to stop execution 22 processExitSpy = vi.spyOn(process, 'exit').mockImplementation((code) => { 23 throw new Error(`process.exit(${code})`); 24 }) as never; 25 }); 26 27 describe('verify command', () => { 28 it('should parse DID from successful SSH response', async () => { 29 // Mock successful SSH response with actual format from tangled.org 30 const mockSshOutput = 31 "Hi @did:plc:b2mcbcamkwyznc5fkplwlxbf! You've successfully authenticated.\n"; 32 33 vi.mocked(execSync).mockImplementation(() => { 34 // ssh -T returns non-zero exit code even on success, throw with stderr 35 const error = new Error('SSH command') as Error & { stderr: string }; 36 error.stderr = mockSshOutput; 37 throw error; 38 }); 39 40 vi.mocked(sessionModule.getCurrentSessionMetadata).mockResolvedValue(null); 41 42 const sshKey = createSshKeyCommand(); 43 await sshKey.parseAsync(['node', 'test', 'verify']); 44 45 expect(consoleLogSpy).toHaveBeenCalledWith( 46 expect.stringContaining('SSH authentication successful') 47 ); 48 expect(consoleLogSpy).toHaveBeenCalledWith( 49 expect.stringContaining('did:plc:b2mcbcamkwyznc5fkplwlxbf') 50 ); 51 expect(consoleLogSpy).toHaveBeenCalledWith( 52 expect.stringContaining('Your SSH setup is working correctly') 53 ); 54 }); 55 56 it('should show handle when logged in user matches SSH DID', async () => { 57 const mockDid = 'did:plc:b2mcbcamkwyznc5fkplwlxbf'; 58 const mockSshOutput = `Hi @${mockDid}! You've successfully authenticated.\n`; 59 60 vi.mocked(execSync).mockImplementation(() => { 61 const error = new Error('SSH command') as Error & { stderr: string }; 62 error.stderr = mockSshOutput; 63 throw error; 64 }); 65 66 vi.mocked(sessionModule.getCurrentSessionMetadata).mockResolvedValue({ 67 handle: 'user.bsky.social', 68 did: mockDid, 69 pds: 'https://bsky.social', 70 lastUsed: new Date().toISOString(), 71 }); 72 73 const sshKey = createSshKeyCommand(); 74 await sshKey.parseAsync(['node', 'test', 'verify']); 75 76 expect(consoleLogSpy).toHaveBeenCalledWith( 77 expect.stringContaining('did:plc:b2mcbcamkwyznc5fkplwlxbf') 78 ); 79 expect(consoleLogSpy).toHaveBeenCalledWith(expect.stringContaining('@user.bsky.social')); 80 }); 81 82 it('should not show handle when logged in user does not match SSH DID', async () => { 83 const mockSshOutput = 84 "Hi @did:plc:b2mcbcamkwyznc5fkplwlxbf! You've successfully authenticated.\n"; 85 86 vi.mocked(execSync).mockImplementation(() => { 87 const error = new Error('SSH command') as Error & { stderr: string }; 88 error.stderr = mockSshOutput; 89 throw error; 90 }); 91 92 vi.mocked(sessionModule.getCurrentSessionMetadata).mockResolvedValue({ 93 handle: 'otheruser.bsky.social', 94 did: 'did:plc:differentuser', 95 pds: 'https://bsky.social', 96 lastUsed: new Date().toISOString(), 97 }); 98 99 const sshKey = createSshKeyCommand(); 100 await sshKey.parseAsync(['node', 'test', 'verify']); 101 102 expect(consoleLogSpy).toHaveBeenCalledWith( 103 expect.stringContaining('did:plc:b2mcbcamkwyznc5fkplwlxbf') 104 ); 105 expect(consoleLogSpy).not.toHaveBeenCalledWith( 106 expect.stringContaining('@otheruser.bsky.social') 107 ); 108 }); 109 110 it('should handle SSH authentication failure', async () => { 111 const mockSshOutput = 'Permission denied (publickey).\n'; 112 113 vi.mocked(execSync).mockImplementation(() => { 114 const error = new Error('SSH command') as Error & { stderr: string }; 115 error.stderr = mockSshOutput; 116 throw error; 117 }); 118 119 const sshKey = createSshKeyCommand(); 120 await expect(sshKey.parseAsync(['node', 'test', 'verify'])).rejects.toThrow('process.exit'); 121 122 expect(consoleErrorSpy).toHaveBeenCalledWith( 123 expect.stringContaining('SSH authentication failed') 124 ); 125 expect(consoleErrorSpy).toHaveBeenCalledWith( 126 expect.stringContaining('Could not find authenticated DID') 127 ); 128 expect(processExitSpy).toHaveBeenCalledWith(1); 129 }); 130 131 it('should provide helpful error message on failure', async () => { 132 const mockSshOutput = 'Connection refused'; 133 134 vi.mocked(execSync).mockImplementation(() => { 135 const error = new Error('SSH command') as Error & { stderr: string }; 136 error.stderr = mockSshOutput; 137 throw error; 138 }); 139 140 const sshKey = createSshKeyCommand(); 141 await expect(sshKey.parseAsync(['node', 'test', 'verify'])).rejects.toThrow('process.exit'); 142 143 expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining('Generated an SSH key')); 144 expect(consoleErrorSpy).toHaveBeenCalledWith( 145 expect.stringContaining('tangled.org/settings/keys') 146 ); 147 expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining('SSH agent is running')); 148 }); 149 }); 150});