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! :)
1import { beforeEach, describe, expect, it, vi } from 'vitest';
2import { createContextCommand } from '../../src/commands/context.js';
3
4// Mock modules
5vi.mock('../../src/lib/context.js');
6
7// Import mocked modules
8import * as contextModule from '../../src/lib/context.js';
9
10describe('Context Command', () => {
11 let consoleLogSpy: ReturnType<typeof vi.fn>;
12 let consoleErrorSpy: 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 so tests don't actually exit
22 vi.spyOn(process, 'exit').mockImplementation((code) => {
23 throw new Error(`process.exit(${code})`);
24 }) as never;
25 });
26
27 it('should display repository context when in tangled repo', async () => {
28 vi.mocked(contextModule.getCurrentRepoContext).mockResolvedValue({
29 owner: 'did:plc:b2mcbcamkwyznc5fkplwlxbf',
30 ownerType: 'did',
31 name: 'tangled-cli',
32 remoteName: 'origin',
33 remoteUrl: 'git@tangled.org:did:plc:b2mcbcamkwyznc5fkplwlxbf/tangled-cli.git',
34 protocol: 'ssh',
35 });
36
37 const context = createContextCommand();
38 await context.parseAsync(['node', 'test']);
39
40 expect(consoleLogSpy).toHaveBeenCalledWith('✓ Repository context resolved:\n');
41 expect(consoleLogSpy).toHaveBeenCalledWith(' Owner: did:plc:b2mcbcamkwyznc5fkplwlxbf (did)');
42 expect(consoleLogSpy).toHaveBeenCalledWith(' Repository: tangled-cli');
43 expect(consoleLogSpy).toHaveBeenCalledWith(' Protocol: ssh');
44 expect(consoleLogSpy).toHaveBeenCalledWith(
45 ' Remote: origin (git@tangled.org:did:plc:b2mcbcamkwyznc5fkplwlxbf/tangled-cli.git)'
46 );
47 });
48
49 it('should display context for HTTPS remote with handle', async () => {
50 vi.mocked(contextModule.getCurrentRepoContext).mockResolvedValue({
51 owner: 'markbennett.ca',
52 ownerType: 'handle',
53 name: 'tangled-cli',
54 remoteName: 'origin',
55 remoteUrl: 'https://tangled.org/markbennett.ca/tangled-cli',
56 protocol: 'https',
57 });
58
59 const context = createContextCommand();
60 await context.parseAsync(['node', 'test']);
61
62 expect(consoleLogSpy).toHaveBeenCalledWith(' Owner: markbennett.ca (handle)');
63 expect(consoleLogSpy).toHaveBeenCalledWith(' Protocol: https');
64 });
65
66 it('should show error when not in tangled repository', async () => {
67 vi.mocked(contextModule.getCurrentRepoContext).mockResolvedValue(null);
68
69 const context = createContextCommand();
70 await expect(context.parseAsync(['node', 'test'])).rejects.toThrow('process.exit(1)');
71
72 expect(consoleLogSpy).toHaveBeenCalledWith('✗ Not in a Tangled repository');
73 expect(consoleLogSpy).toHaveBeenCalledWith(
74 expect.stringContaining('To use this repository with Tangled')
75 );
76 expect(consoleLogSpy).toHaveBeenCalledWith(
77 expect.stringContaining('git remote add origin git@tangled.org:')
78 );
79 });
80
81 it('should handle errors gracefully', async () => {
82 vi.mocked(contextModule.getCurrentRepoContext).mockRejectedValue(new Error('Git error'));
83
84 const context = createContextCommand();
85 await expect(context.parseAsync(['node', 'test'])).rejects.toThrow('process.exit(1)');
86
87 expect(consoleErrorSpy).toHaveBeenCalledWith(
88 expect.stringContaining('Failed to resolve context')
89 );
90 expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining('Git error'));
91 });
92});