git-drive 0.1.6 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/.github/workflows/ci.yml +77 -0
  2. package/.planning/codebase/ARCHITECTURE.md +151 -0
  3. package/.planning/codebase/CONCERNS.md +191 -0
  4. package/.planning/codebase/CONVENTIONS.md +169 -0
  5. package/.planning/codebase/INTEGRATIONS.md +94 -0
  6. package/.planning/codebase/STACK.md +77 -0
  7. package/.planning/codebase/STRUCTURE.md +157 -0
  8. package/.planning/codebase/TESTING.md +156 -0
  9. package/Dockerfile.cli +30 -0
  10. package/Dockerfile.server +32 -0
  11. package/README.md +157 -0
  12. package/docker-compose.yml +48 -0
  13. package/package.json +20 -55
  14. package/packages/cli/Dockerfile +26 -0
  15. package/packages/cli/jest.config.js +26 -0
  16. package/packages/cli/package.json +65 -0
  17. package/packages/cli/src/__tests__/commands/companion.test.ts +152 -0
  18. package/packages/cli/src/__tests__/commands/init.test.ts +154 -0
  19. package/packages/cli/src/__tests__/commands/list.test.ts +122 -0
  20. package/packages/cli/src/__tests__/commands/push.test.ts +155 -0
  21. package/packages/cli/src/__tests__/commands/restore.test.ts +135 -0
  22. package/packages/cli/src/__tests__/commands/status.test.ts +199 -0
  23. package/packages/cli/src/__tests__/config.test.ts +198 -0
  24. package/packages/cli/src/__tests__/e2e.test.ts +125 -0
  25. package/packages/cli/src/__tests__/errors.test.ts +66 -0
  26. package/packages/cli/src/__tests__/git.test.ts +250 -0
  27. package/packages/cli/src/__tests__/server.test.ts +371 -0
  28. package/packages/cli/src/commands/archive.ts +39 -0
  29. package/packages/cli/src/commands/companion.ts +205 -0
  30. package/packages/cli/src/commands/init.ts +130 -0
  31. package/packages/cli/src/commands/link.ts +151 -0
  32. package/packages/cli/src/commands/list.ts +94 -0
  33. package/packages/cli/src/commands/push.ts +77 -0
  34. package/packages/cli/src/commands/restore.ts +36 -0
  35. package/packages/cli/src/commands/status.ts +127 -0
  36. package/packages/cli/src/config.ts +73 -0
  37. package/packages/cli/src/errors.ts +23 -0
  38. package/packages/cli/src/git.ts +60 -0
  39. package/packages/cli/src/index.ts +129 -0
  40. package/packages/cli/src/server.ts +700 -0
  41. package/packages/cli/tsconfig.json +13 -0
  42. package/packages/git-drive-docker/package.json +15 -0
  43. package/packages/server/package.json +44 -0
  44. package/packages/server/src/index.ts +569 -0
  45. package/packages/server/tsconfig.json +9 -0
  46. package/packages/ui/README.md +73 -0
  47. package/packages/ui/eslint.config.js +23 -0
  48. package/packages/ui/index.html +13 -0
  49. package/packages/ui/package.json +52 -0
  50. package/packages/ui/postcss.config.js +6 -0
  51. package/packages/ui/public/vite.svg +1 -0
  52. package/packages/ui/src/App.css +23 -0
  53. package/packages/ui/src/App.test.tsx +248 -0
  54. package/packages/ui/src/App.tsx +803 -0
  55. package/packages/ui/src/assets/react.svg +8 -0
  56. package/packages/ui/src/assets/vite.svg +3 -0
  57. package/packages/ui/src/index.css +37 -0
  58. package/packages/ui/src/main.tsx +14 -0
  59. package/packages/ui/src/test/setup.ts +1 -0
  60. package/packages/ui/tailwind.config.js +11 -0
  61. package/packages/ui/tsconfig.app.json +28 -0
  62. package/packages/ui/tsconfig.json +26 -0
  63. package/packages/ui/tsconfig.node.json +12 -0
  64. package/packages/ui/vite.config.ts +7 -0
  65. package/packages/ui/vitest.config.ts +20 -0
  66. package/pnpm-workspace.yaml +4 -0
  67. package/rewrite_app.js +731 -0
  68. package/tsconfig.json +14 -0
  69. package/dist/__tests__/commands/init.test.js +0 -123
  70. package/dist/__tests__/commands/list.test.js +0 -91
  71. package/dist/__tests__/commands/push.test.js +0 -128
  72. package/dist/__tests__/commands/restore.test.js +0 -99
  73. package/dist/__tests__/commands/status.test.js +0 -151
  74. package/dist/__tests__/config.test.js +0 -150
  75. package/dist/__tests__/e2e.test.js +0 -107
  76. package/dist/__tests__/errors.test.js +0 -56
  77. package/dist/__tests__/git.test.js +0 -184
  78. package/dist/__tests__/server.test.js +0 -310
  79. package/dist/commands/archive.js +0 -32
  80. package/dist/commands/init.js +0 -55
  81. package/dist/commands/link.js +0 -175
  82. package/dist/commands/list.js +0 -83
  83. package/dist/commands/push.js +0 -112
  84. package/dist/commands/restore.js +0 -30
  85. package/dist/commands/status.js +0 -116
  86. package/dist/config.js +0 -62
  87. package/dist/errors.js +0 -30
  88. package/dist/git.js +0 -67
  89. package/dist/index.js +0 -108
  90. package/dist/server.js +0 -535
  91. /package/{ui → packages/cli/ui}/assets/index-Br8xQbJz.js +0 -0
  92. /package/{ui → packages/cli/ui}/assets/index-Cc2q1t5k.js +0 -0
  93. /package/{ui → packages/cli/ui}/assets/index-DrL7ojPA.css +0 -0
  94. /package/{ui → packages/cli/ui}/index.html +0 -0
  95. /package/{ui → packages/cli/ui}/vite.svg +0 -0
@@ -1,150 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const memfs_1 = require("memfs");
4
- const path_1 = require("path");
5
- const config_js_1 = require("../config.js");
6
- // Mock fs and os modules
7
- jest.mock('fs', () => {
8
- const { fs } = require('memfs');
9
- return fs;
10
- });
11
- jest.mock('os', () => ({
12
- homedir: () => '/home/testuser',
13
- }));
14
- describe('config', () => {
15
- const configDir = '/home/testuser/.config/git-drive';
16
- const configFile = (0, path_1.join)(configDir, 'config.json');
17
- const linksFile = (0, path_1.join)(configDir, 'links.json');
18
- beforeEach(() => {
19
- memfs_1.vol.reset();
20
- });
21
- describe('loadConfig', () => {
22
- it('should return null when config file does not exist', () => {
23
- const result = (0, config_js_1.loadConfig)();
24
- expect(result).toBeNull();
25
- });
26
- it('should load and parse valid config', () => {
27
- const config = { drivePath: '/Volumes/TestDrive' };
28
- memfs_1.vol.fromJSON({
29
- [configFile]: JSON.stringify(config),
30
- });
31
- const result = (0, config_js_1.loadConfig)();
32
- expect(result).toEqual(config);
33
- });
34
- it('should throw on malformed JSON', () => {
35
- memfs_1.vol.fromJSON({
36
- [configFile]: 'not valid json',
37
- });
38
- expect(() => (0, config_js_1.loadConfig)()).toThrow();
39
- });
40
- });
41
- describe('saveConfig', () => {
42
- it('should create config directory if it does not exist', () => {
43
- const config = { drivePath: '/Volumes/MyDrive' };
44
- (0, config_js_1.saveConfig)(config);
45
- const files = memfs_1.vol.toJSON();
46
- expect(files[configFile]).toBeDefined();
47
- });
48
- it('should save config with proper formatting', () => {
49
- const config = { drivePath: '/Volumes/MyDrive' };
50
- (0, config_js_1.saveConfig)(config);
51
- const savedContent = memfs_1.vol.toJSON()[configFile];
52
- expect(savedContent).toBe(JSON.stringify(config, null, 2) + '\n');
53
- });
54
- it('should overwrite existing config', () => {
55
- const config1 = { drivePath: '/Volumes/Drive1' };
56
- const config2 = { drivePath: '/Volumes/Drive2' };
57
- (0, config_js_1.saveConfig)(config1);
58
- (0, config_js_1.saveConfig)(config2);
59
- const result = (0, config_js_1.loadConfig)();
60
- expect(result).toEqual(config2);
61
- });
62
- });
63
- describe('requireConfig', () => {
64
- it('should return config when it exists', () => {
65
- const config = { drivePath: '/Volumes/TestDrive' };
66
- memfs_1.vol.fromJSON({
67
- [configFile]: JSON.stringify(config),
68
- });
69
- const result = (0, config_js_1.requireConfig)();
70
- expect(result).toEqual(config);
71
- });
72
- it('should throw GitDriveError when config does not exist', () => {
73
- expect(() => (0, config_js_1.requireConfig)()).toThrow('No drive configured. Run: git drive init <path>');
74
- });
75
- });
76
- describe('assertDriveMounted', () => {
77
- it('should not throw when drive path exists', () => {
78
- memfs_1.vol.fromJSON({
79
- '/Volumes/TestDrive/.git-drive': '',
80
- });
81
- expect(() => (0, config_js_1.assertDriveMounted)('/Volumes/TestDrive')).not.toThrow();
82
- });
83
- it('should throw GitDriveError when drive path does not exist', () => {
84
- expect(() => (0, config_js_1.assertDriveMounted)('/Volumes/NonExistent')).toThrow('Drive not found at /Volumes/NonExistent. Is it connected?');
85
- });
86
- });
87
- describe('getDriveStorePath', () => {
88
- it('should return the .git-drive path for a mountpoint', () => {
89
- expect((0, config_js_1.getDriveStorePath)('/Volumes/MyDrive')).toBe('/Volumes/MyDrive/.git-drive');
90
- });
91
- it('should handle different path formats', () => {
92
- expect((0, config_js_1.getDriveStorePath)('/mnt/usb')).toBe('/mnt/usb/.git-drive');
93
- });
94
- });
95
- describe('loadLinks', () => {
96
- it('should return empty object when links file does not exist', () => {
97
- const result = (0, config_js_1.loadLinks)();
98
- expect(result).toEqual({});
99
- });
100
- it('should load and parse valid links', () => {
101
- const links = {
102
- '/home/user/project1': {
103
- mountpoint: '/Volumes/Drive1',
104
- repoName: 'project1.git',
105
- linkedAt: '2024-01-01T00:00:00.000Z',
106
- },
107
- };
108
- memfs_1.vol.fromJSON({
109
- [linksFile]: JSON.stringify(links),
110
- });
111
- const result = (0, config_js_1.loadLinks)();
112
- expect(result).toEqual(links);
113
- });
114
- it('should return empty object on malformed JSON', () => {
115
- memfs_1.vol.fromJSON({
116
- [linksFile]: 'invalid json',
117
- });
118
- const result = (0, config_js_1.loadLinks)();
119
- expect(result).toEqual({});
120
- });
121
- });
122
- describe('saveLink', () => {
123
- it('should create links directory if it does not exist', () => {
124
- (0, config_js_1.saveLink)('/home/user/project', '/Volumes/Drive', 'project.git');
125
- const files = memfs_1.vol.toJSON();
126
- expect(files[linksFile]).toBeDefined();
127
- });
128
- it('should save a new link', () => {
129
- (0, config_js_1.saveLink)('/home/user/project', '/Volumes/Drive', 'project.git');
130
- const result = (0, config_js_1.loadLinks)();
131
- expect(result['/home/user/project']).toEqual({
132
- mountpoint: '/Volumes/Drive',
133
- repoName: 'project.git',
134
- linkedAt: expect.any(String),
135
- });
136
- });
137
- it('should update existing link', () => {
138
- (0, config_js_1.saveLink)('/home/user/project', '/Volumes/Drive1', 'project.git');
139
- (0, config_js_1.saveLink)('/home/user/project', '/Volumes/Drive2', 'project.git');
140
- const result = (0, config_js_1.loadLinks)();
141
- expect(result['/home/user/project'].mountpoint).toBe('/Volumes/Drive2');
142
- });
143
- it('should preserve other links when adding new one', () => {
144
- (0, config_js_1.saveLink)('/home/user/project1', '/Volumes/Drive', 'project1.git');
145
- (0, config_js_1.saveLink)('/home/user/project2', '/Volumes/Drive', 'project2.git');
146
- const result = (0, config_js_1.loadLinks)();
147
- expect(Object.keys(result)).toHaveLength(2);
148
- });
149
- });
150
- });
@@ -1,107 +0,0 @@
1
- "use strict";
2
- /**
3
- * E2E Tests for git-drive CLI
4
- *
5
- * These tests simulate full command workflows using mocked file systems
6
- * and git operations to test the complete flow of the CLI.
7
- */
8
- Object.defineProperty(exports, "__esModule", { value: true });
9
- const memfs_1 = require("memfs");
10
- // Mock fs
11
- jest.mock('fs', () => {
12
- const { fs } = require('memfs');
13
- return fs;
14
- });
15
- // Mock child_process
16
- jest.mock('child_process', () => ({
17
- execSync: jest.fn(),
18
- spawn: jest.fn(() => ({
19
- on: jest.fn(),
20
- unref: jest.fn(),
21
- })),
22
- }));
23
- // Mock node-disk-info
24
- jest.mock('node-disk-info', () => ({
25
- getDiskInfo: jest.fn(),
26
- }));
27
- // Mock prompts
28
- jest.mock('prompts', () => ({
29
- __esModule: true,
30
- default: jest.fn(),
31
- }));
32
- // Mock os
33
- jest.mock('os', () => ({
34
- homedir: () => '/home/testuser',
35
- hostname: () => 'test-machine',
36
- userInfo: () => ({ username: 'testuser' }),
37
- }));
38
- const node_disk_info_1 = require("node-disk-info");
39
- const child_process_1 = require("child_process");
40
- const mockExecSync = child_process_1.execSync;
41
- const mockGetDiskInfo = node_disk_info_1.getDiskInfo;
42
- describe('E2E: Full Workflow', () => {
43
- let consoleSpy;
44
- const originalPlatform = process.platform;
45
- beforeEach(() => {
46
- jest.clearAllMocks();
47
- memfs_1.vol.reset();
48
- consoleSpy = jest.spyOn(console, 'log').mockImplementation();
49
- Object.defineProperty(process, 'platform', { value: 'darwin', writable: true });
50
- mockGetDiskInfo.mockResolvedValue([
51
- { mounted: '/Volumes/TestDrive', filesystem: 'TestDrive', blocks: 32000000, available: 16000000 },
52
- ]);
53
- mockExecSync.mockImplementation((cmd) => {
54
- if (cmd.includes('rev-parse --show-toplevel'))
55
- return '/home/testuser/my-project';
56
- if (cmd.includes('branch --show-current'))
57
- return 'main';
58
- if (cmd.includes('rev-parse --is-inside-work-tree'))
59
- return 'true';
60
- return '';
61
- });
62
- });
63
- afterEach(() => {
64
- consoleSpy.mockRestore();
65
- Object.defineProperty(process, 'platform', { value: originalPlatform, writable: true });
66
- });
67
- describe('Complete Backup Workflow', () => {
68
- it('should verify test infrastructure is working', async () => {
69
- expect(true).toBe(true);
70
- });
71
- it('should mock disk info correctly', async () => {
72
- const drives = await mockGetDiskInfo();
73
- expect(drives).toHaveLength(1);
74
- expect(drives[0].mounted).toBe('/Volumes/TestDrive');
75
- });
76
- });
77
- describe('Error Recovery', () => {
78
- it('should handle errors gracefully', async () => {
79
- mockGetDiskInfo.mockRejectedValue(new Error('Failed to get drives'));
80
- const drives = await mockGetDiskInfo().catch((e) => e.message);
81
- expect(drives).toBe('Failed to get drives');
82
- });
83
- });
84
- describe('Multi-Drive Support', () => {
85
- it('should handle multiple drives', async () => {
86
- mockGetDiskInfo.mockResolvedValue([
87
- { mounted: '/Volumes/Drive1', filesystem: 'Drive1', blocks: 32000000, available: 16000000 },
88
- { mounted: '/Volumes/Drive2', filesystem: 'Drive2', blocks: 64000000, available: 32000000 },
89
- ]);
90
- const drives = await mockGetDiskInfo();
91
- expect(drives).toHaveLength(2);
92
- });
93
- });
94
- });
95
- describe('E2E: CLI Entry Point', () => {
96
- it('should display help when no arguments provided', () => {
97
- expect(true).toBe(true);
98
- });
99
- it('should display version with --version flag', () => {
100
- expect(true).toBe(true);
101
- });
102
- });
103
- describe('E2E: Server Startup', () => {
104
- it('should start the web server', () => {
105
- expect(true).toBe(true);
106
- });
107
- });
@@ -1,56 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const errors_js_1 = require("../errors.js");
4
- describe('GitDriveError', () => {
5
- it('should create an error with the correct message', () => {
6
- const error = new errors_js_1.GitDriveError('Something went wrong');
7
- expect(error).toBeInstanceOf(Error);
8
- expect(error.message).toBe('Something went wrong');
9
- expect(error.name).toBe('GitDriveError');
10
- });
11
- it('should create an error with an empty message', () => {
12
- const error = new errors_js_1.GitDriveError('');
13
- expect(error.message).toBe('');
14
- expect(error.name).toBe('GitDriveError');
15
- });
16
- });
17
- describe('handleError', () => {
18
- let consoleSpy;
19
- beforeEach(() => {
20
- consoleSpy = jest.spyOn(console, 'error').mockImplementation();
21
- });
22
- afterEach(() => {
23
- consoleSpy.mockRestore();
24
- });
25
- it('should handle GitDriveError', () => {
26
- const error = new errors_js_1.GitDriveError('Custom git-drive error');
27
- (0, errors_js_1.handleError)(error);
28
- expect(consoleSpy).toHaveBeenCalledWith('error: Custom git-drive error');
29
- });
30
- it('should handle regular Error', () => {
31
- const error = new Error('Regular error message');
32
- (0, errors_js_1.handleError)(error);
33
- expect(consoleSpy).toHaveBeenCalledWith('error: Regular error message');
34
- });
35
- it('should extract stderr from execSync error message', () => {
36
- const error = new Error('Command failed: git push\nstderr: fatal: not a git repository');
37
- (0, errors_js_1.handleError)(error);
38
- expect(consoleSpy).toHaveBeenCalledWith('error: fatal: not a git repository');
39
- });
40
- it('should handle unknown error types', () => {
41
- (0, errors_js_1.handleError)('string error');
42
- expect(consoleSpy).toHaveBeenCalledWith('An unexpected error occurred.');
43
- });
44
- it('should handle null error', () => {
45
- (0, errors_js_1.handleError)(null);
46
- expect(consoleSpy).toHaveBeenCalledWith('An unexpected error occurred.');
47
- });
48
- it('should handle undefined error', () => {
49
- (0, errors_js_1.handleError)(undefined);
50
- expect(consoleSpy).toHaveBeenCalledWith('An unexpected error occurred.');
51
- });
52
- it('should handle object error', () => {
53
- (0, errors_js_1.handleError)({ code: 'ERR_SOMETHING' });
54
- expect(consoleSpy).toHaveBeenCalledWith('An unexpected error occurred.');
55
- });
56
- });
@@ -1,184 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const git_js_1 = require("../git.js");
4
- // Mock child_process
5
- jest.mock('child_process', () => ({
6
- execSync: jest.fn(),
7
- }));
8
- // Mock node-disk-info
9
- jest.mock('node-disk-info', () => ({
10
- getDiskInfo: jest.fn(),
11
- }));
12
- const child_process_1 = require("child_process");
13
- const node_disk_info_1 = require("node-disk-info");
14
- const mockExecSync = child_process_1.execSync;
15
- const mockGetDiskInfo = node_disk_info_1.getDiskInfo;
16
- describe('git', () => {
17
- beforeEach(() => {
18
- jest.clearAllMocks();
19
- });
20
- describe('git function', () => {
21
- it('should execute git command and return trimmed output', () => {
22
- mockExecSync.mockReturnValue(' output from git \n');
23
- const result = (0, git_js_1.git)('status --short');
24
- expect(mockExecSync).toHaveBeenCalledWith('git status --short', {
25
- cwd: undefined,
26
- encoding: 'utf-8',
27
- stdio: ['pipe', 'pipe', 'pipe'],
28
- });
29
- expect(result).toBe('output from git');
30
- });
31
- it('should execute git command with cwd option', () => {
32
- mockExecSync.mockReturnValue('output');
33
- const result = (0, git_js_1.git)('status', '/path/to/repo');
34
- expect(mockExecSync).toHaveBeenCalledWith('git status', {
35
- cwd: '/path/to/repo',
36
- encoding: 'utf-8',
37
- stdio: ['pipe', 'pipe', 'pipe'],
38
- });
39
- expect(result).toBe('output');
40
- });
41
- it('should propagate errors from git command', () => {
42
- mockExecSync.mockImplementation(() => {
43
- throw new Error('fatal: not a git repository');
44
- });
45
- expect(() => (0, git_js_1.git)('status')).toThrow('fatal: not a git repository');
46
- });
47
- });
48
- describe('listDrives', () => {
49
- const originalPlatform = process.platform;
50
- afterEach(() => {
51
- Object.defineProperty(process, 'platform', { value: originalPlatform });
52
- });
53
- it('should filter drives correctly on macOS', async () => {
54
- Object.defineProperty(process, 'platform', { value: 'darwin' });
55
- mockGetDiskInfo.mockResolvedValue([
56
- { mounted: '/', filesystem: 'Macintosh HD', blocks: 500000000, available: 100000000 },
57
- { mounted: '/Volumes/MyUSB', filesystem: 'MyUSB', blocks: 32000000, available: 16000000 },
58
- { mounted: '/Volumes/Recovery', filesystem: 'Recovery', blocks: 1000000, available: 500000 },
59
- { mounted: '/Volumes/External', filesystem: 'External', blocks: 1000000000, available: 500000000 },
60
- ]);
61
- const result = await (0, git_js_1.listDrives)();
62
- expect(result).toHaveLength(2);
63
- expect(result.map((d) => d.mounted)).toEqual(['/Volumes/MyUSB', '/Volumes/External']);
64
- });
65
- it('should filter drives correctly on Linux', async () => {
66
- Object.defineProperty(process, 'platform', { value: 'linux' });
67
- mockGetDiskInfo.mockResolvedValue([
68
- { mounted: '/', filesystem: 'root', blocks: 500000000, available: 100000000 },
69
- { mounted: '/mnt/usb', filesystem: 'usbdrive', blocks: 32000000, available: 16000000 },
70
- { mounted: '/sys', filesystem: 'sysfs', blocks: 0, available: 0 },
71
- { mounted: '/proc', filesystem: 'proc', blocks: 0, available: 0 },
72
- { mounted: '/run', filesystem: 'tmpfs', blocks: 1000000, available: 500000 },
73
- { mounted: '/media/user/external', filesystem: 'external', blocks: 1000000000, available: 500000000 },
74
- ]);
75
- const result = await (0, git_js_1.listDrives)();
76
- expect(result).toHaveLength(2);
77
- expect(result.map((d) => d.mounted)).toEqual(['/mnt/usb', '/media/user/external']);
78
- });
79
- it('should filter out tmpfs and overlay filesystems', async () => {
80
- Object.defineProperty(process, 'platform', { value: 'linux' });
81
- mockGetDiskInfo.mockResolvedValue([
82
- { mounted: '/mnt/real', filesystem: 'ext4', blocks: 1000000, available: 500000 },
83
- { mounted: '/mnt/tmpfs', filesystem: 'tmpfs', blocks: 1000000, available: 500000 },
84
- { mounted: '/mnt/devtmpfs', filesystem: 'devtmpfs', blocks: 1000000, available: 500000 },
85
- { mounted: '/mnt/overlay', filesystem: 'overlay', blocks: 1000000, available: 500000 },
86
- { mounted: '/mnt/udev', filesystem: 'udev', blocks: 1000000, available: 500000 },
87
- ]);
88
- const result = await (0, git_js_1.listDrives)();
89
- expect(result).toHaveLength(1);
90
- expect(result[0].mounted).toBe('/mnt/real');
91
- });
92
- it('should filter out drives without mountpoint', async () => {
93
- Object.defineProperty(process, 'platform', { value: 'darwin' });
94
- mockGetDiskInfo.mockResolvedValue([
95
- { mounted: null, filesystem: 'nomount', blocks: 1000000, available: 500000 },
96
- { mounted: '/Volumes/Valid', filesystem: 'valid', blocks: 1000000, available: 500000 },
97
- ]);
98
- const result = await (0, git_js_1.listDrives)();
99
- expect(result).toHaveLength(1);
100
- });
101
- it('should filter out drives with mounted value of "100%"', async () => {
102
- Object.defineProperty(process, 'platform', { value: 'darwin' });
103
- mockGetDiskInfo.mockResolvedValue([
104
- { mounted: '100%', filesystem: 'weird', blocks: 1000000, available: 500000 },
105
- { mounted: '/Volumes/Valid', filesystem: 'valid', blocks: 1000000, available: 500000 },
106
- ]);
107
- const result = await (0, git_js_1.listDrives)();
108
- expect(result).toHaveLength(1);
109
- });
110
- it('should return empty array when no drives match', async () => {
111
- Object.defineProperty(process, 'platform', { value: 'darwin' });
112
- mockGetDiskInfo.mockResolvedValue([
113
- { mounted: '/', filesystem: 'system', blocks: 500000000, available: 100000000 },
114
- ]);
115
- const result = await (0, git_js_1.listDrives)();
116
- expect(result).toHaveLength(0);
117
- });
118
- it('should filter out temporary paths with TemporaryItems', async () => {
119
- Object.defineProperty(process, 'platform', { value: 'darwin' });
120
- mockGetDiskInfo.mockResolvedValue([
121
- { mounted: '/var/folders/2b/b7kfzb0s2v55m89_k9qvpj9w0000gn/T/TemporaryItems/NSIRD_screencaptureui_g99XDe', filesystem: 'tmpfs', blocks: 1000, available: 500 },
122
- ]);
123
- const result = await (0, git_js_1.listDrives)();
124
- expect(result).toHaveLength(0);
125
- });
126
- it('should filter out /var/folders paths', async () => {
127
- Object.defineProperty(process, 'platform', { value: 'darwin' });
128
- mockGetDiskInfo.mockResolvedValue([
129
- { mounted: '/var/folders/abc', filesystem: 'tmpfs', blocks: 1000, available: 500 },
130
- ]);
131
- const result = await (0, git_js_1.listDrives)();
132
- expect(result).toHaveLength(0);
133
- });
134
- });
135
- describe('getRepoRoot', () => {
136
- it('should return the repo root path', () => {
137
- mockExecSync.mockReturnValue('/path/to/repo\n');
138
- const result = (0, git_js_1.getRepoRoot)();
139
- expect(mockExecSync).toHaveBeenCalledWith('git rev-parse --show-toplevel', expect.any(Object));
140
- expect(result).toBe('/path/to/repo');
141
- });
142
- });
143
- describe('getProjectName', () => {
144
- it('should return the basename of the repo root', () => {
145
- mockExecSync.mockReturnValue('/path/to/my-project');
146
- const result = (0, git_js_1.getProjectName)();
147
- expect(result).toBe('my-project');
148
- });
149
- it('should handle nested paths', () => {
150
- mockExecSync.mockReturnValue('/Users/developer/projects/awesome-app');
151
- const result = (0, git_js_1.getProjectName)();
152
- expect(result).toBe('awesome-app');
153
- });
154
- });
155
- describe('getRemoteUrl', () => {
156
- it('should return the remote URL if it exists', () => {
157
- mockExecSync.mockReturnValue('/Volumes/MyDrive/.git-drive/my-project.git');
158
- const result = (0, git_js_1.getRemoteUrl)('gd');
159
- expect(mockExecSync).toHaveBeenCalledWith('git remote get-url gd', expect.any(Object));
160
- expect(result).toBe('/Volumes/MyDrive/.git-drive/my-project.git');
161
- });
162
- it('should return null if remote does not exist', () => {
163
- mockExecSync.mockImplementation(() => {
164
- throw new Error('fatal: No such remote');
165
- });
166
- const result = (0, git_js_1.getRemoteUrl)('nonexistent');
167
- expect(result).toBeNull();
168
- });
169
- });
170
- describe('isGitRepo', () => {
171
- it('should return true when in a git repository', () => {
172
- mockExecSync.mockReturnValue('true');
173
- const result = (0, git_js_1.isGitRepo)();
174
- expect(result).toBe(true);
175
- });
176
- it('should return false when not in a git repository', () => {
177
- mockExecSync.mockImplementation(() => {
178
- throw new Error('fatal: not a git repository');
179
- });
180
- const result = (0, git_js_1.isGitRepo)();
181
- expect(result).toBe(false);
182
- });
183
- });
184
- });