gitforest 0.1.0 → 1.1.0
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.
- package/LICENSE +21 -0
- package/package.json +24 -4
- package/src/components/onboarding/DirectoriesStep.tsx +19 -19
- package/src/github/auth.ts +3 -3
- package/src/utils/debug.ts +4 -4
- package/.bunignore +0 -7
- package/.github/workflows/ci.yml +0 -73
- package/CLAUDE.md +0 -111
- package/CONTRIBUTING.md +0 -145
- package/bun.lock +0 -267
- package/bunfig.toml +0 -15
- package/cli +0 -0
- package/docs/ai/IMPROVEMENT_PLAN.md +0 -341
- package/docs/ai/VERIFICATION_REPORT.md +0 -87
- package/docs/ai/architecture.md +0 -169
- package/docs/ai/checks/check-2025-12-02-tests.md +0 -40
- package/docs/ai/checks/check-2025-12-02.md +0 -55
- package/docs/ai/checks/test-verification-report.md +0 -85
- package/docs/ai/implementation-guide.md +0 -776
- package/docs/ai/research/gitty-codebase-analysis.md +0 -221
- package/docs/ai/tickets/GENERAL-sitrep.md +0 -30
- package/docs/ai/tickets/TASK-database-tests-sitrep.md +0 -25
- package/docs/ai/tickets/TASK-deprecated-functions-sitrep.md +0 -28
- package/docs/ai/tickets/TASK-detail-modal-sitrep.md +0 -28
- package/docs/ai/tickets/TASK-filter-overlay-sitrep.md +0 -24
- package/docs/ai/tickets/TASK-github-service-sitrep.md +0 -32
- package/docs/ai/tickets/TASK-github-token-sitrep.md +0 -51
- package/docs/ai/tickets/TASK-hascommits-sitrep.md +0 -35
- package/docs/ai/tickets/TASK-keybindings-sitrep.md +0 -26
- package/docs/ai/tickets/TASK-layout-sitrep.md +0 -25
- package/docs/ai/tickets/TASK-markdown-sitrep.md +0 -28
- package/docs/ai/tickets/TASK-project-item-sitrep.md +0 -79
- package/docs/ai/tickets/TASK-sitrep.md +0 -28
- package/docs/ai/tickets/TASK-state-sitrep.md +0 -26
- package/docs/ai/tickets/TASK-types-sitrep.md +0 -25
- package/docs/ai/tickets/TASK-unified-item-fix-sitrep.md +0 -26
- package/docs/ai/tickets/TKT-001-sitrep.md +0 -24
- package/docs/ai/tickets/TKT-002-sitrep.md +0 -25
- package/docs/ai/tickets/TKT-003-git-service-refactoring-complete.md +0 -46
- package/docs/ai/tickets/TKT-003-git-service-refactoring-plan.md +0 -135
- package/docs/ai/tickets/TKT-003-sitrep.md +0 -26
- package/docs/ai/tickets/TKT-004-sitrep.md +0 -27
- package/docs/ai/tickets/TKT-005-sitrep.md +0 -25
- package/docs/ai/tickets/TKT-006-sitrep.md +0 -26
- package/docs/ai/tickets/TKT-007-sitrep.md +0 -30
- package/docs/ai/tickets/TKT-008-sitrep.md +0 -32
- package/docs/ai/tickets/TKT-009-sitrep.md +0 -27
- package/docs/ai/tickets/TKT-010-sitrep.md +0 -27
- package/docs/ai/tickets/TKT-011-sitrep.md +0 -26
- package/docs/ai/tickets/TKT-012-sitrep.md +0 -25
- package/docs/ai/tickets/sitreps/TASK-actions-sitrep.md +0 -28
- package/docs/ai/tickets/sitreps/TASK-actions-test-sitrep.md +0 -25
- package/docs/ai/tickets/sitreps/TASK-app-integration-sitrep.md +0 -25
- package/docs/ai/tickets/sitreps/TASK-background-fetch-sitrep.md +0 -24
- package/docs/ai/tickets/sitreps/TASK-background-fetch-test-sitrep.md +0 -29
- package/docs/ai/tickets/sitreps/TASK-batch-tests-sitrep.md +0 -29
- package/docs/ai/tickets/sitreps/TASK-bun-test-sitrep.md +0 -26
- package/docs/ai/tickets/sitreps/TASK-cache-tests-sitrep.md +0 -30
- package/docs/ai/tickets/sitreps/TASK-cli-tests-sitrep.md +0 -28
- package/docs/ai/tickets/sitreps/TASK-clone-error-handling-sitrep.md +0 -26
- package/docs/ai/tickets/sitreps/TASK-commands-tests-sitrep.md +0 -25
- package/docs/ai/tickets/sitreps/TASK-component-tests-1-sitrep.md +0 -30
- package/docs/ai/tickets/sitreps/TASK-configloader-tests-sitrep.md +0 -25
- package/docs/ai/tickets/sitreps/TASK-confirm-dialog-test-sitrep.md +0 -29
- package/docs/ai/tickets/sitreps/TASK-coverage-sitrep.md +0 -95
- package/docs/ai/tickets/sitreps/TASK-database-tests-summary.md +0 -61
- package/docs/ai/tickets/sitreps/TASK-error-boundary-sitrep.md +0 -30
- package/docs/ai/tickets/sitreps/TASK-error-tests-sitrep.md +0 -27
- package/docs/ai/tickets/sitreps/TASK-errors-tests-sitrep.md +0 -25
- package/docs/ai/tickets/sitreps/TASK-extract-reducer-sitrep.md +0 -27
- package/docs/ai/tickets/sitreps/TASK-filter-overlay-test-sitrep.md +0 -25
- package/docs/ai/tickets/sitreps/TASK-final-verification-sitrep.md +0 -28
- package/docs/ai/tickets/sitreps/TASK-fix-all-tests-sitrep.md +0 -25
- package/docs/ai/tickets/sitreps/TASK-fix-hooks-sitrep.md +0 -26
- package/docs/ai/tickets/sitreps/TASK-fix-remaining-tests-sitrep.md +0 -25
- package/docs/ai/tickets/sitreps/TASK-fix-test-failures-sitrep.md +0 -26
- package/docs/ai/tickets/sitreps/TASK-fix-tests-sitrep.md +0 -24
- package/docs/ai/tickets/sitreps/TASK-formatters-tests-sitrep.md +0 -25
- package/docs/ai/tickets/sitreps/TASK-git-timeouts-sitrep.md +0 -29
- package/docs/ai/tickets/sitreps/TASK-github-cache-test-sitrep.md +0 -25
- package/docs/ai/tickets/sitreps/TASK-githubcli-tests-sitrep.md +0 -24
- package/docs/ai/tickets/sitreps/TASK-gitstatus-tests-sitrep.md +0 -24
- package/docs/ai/tickets/sitreps/TASK-hooks-isolation-sitrep.md +0 -27
- package/docs/ai/tickets/sitreps/TASK-keybindings-tests-sitrep.md +0 -25
- package/docs/ai/tickets/sitreps/TASK-layout-tests-sitrep.md +0 -25
- package/docs/ai/tickets/sitreps/TASK-mock-factories-sitrep.md +0 -27
- package/docs/ai/tickets/sitreps/TASK-modal-tests-sitrep.md +0 -32
- package/docs/ai/tickets/sitreps/TASK-processbatch-fix-sitrep.md +0 -27
- package/docs/ai/tickets/sitreps/TASK-projectlist-tests-sitrep.md +0 -30
- package/docs/ai/tickets/sitreps/TASK-projectutils-tests-sitrep.md +0 -25
- package/docs/ai/tickets/sitreps/TASK-scanner-tests-sitrep.md +0 -29
- package/docs/ai/tickets/sitreps/TASK-select-all-sitrep.md +0 -25
- package/docs/ai/tickets/sitreps/TASK-shell-error-handling-sitrep.md +0 -27
- package/docs/ai/tickets/sitreps/TASK-store-tests-sitrep.md +0 -25
- package/docs/ai/tickets/sitreps/TASK-test-fixes-sitrep.md +0 -26
- package/docs/ai/tickets/sitreps/TASK-test-summary-sitrep.md +0 -25
- package/docs/ai/tickets/sitreps/TASK-test-verification-sitrep.md +0 -27
- package/docs/ai/tickets/sitreps/TASK-testsuite-sitrep.md +0 -75
- package/docs/ai/tickets/sitreps/TASK-unified-reducer-tests-sitrep.md +0 -29
- package/docs/ai/tickets/sitreps/TASK-unified-repos-test-sitrep.md +0 -29
- package/docs/ai/tickets/sitreps/TASK-unified-tests-sitrep.md +0 -25
- package/docs/ai/tickets/sitreps/TASK-useprojects-tests-sitrep.md +0 -25
- package/docs/ai/tickets/sitreps/TASK-utility-tests-sitrep.md +0 -32
- package/docs/ai/tickets/sitreps/TKT-003-git-service-refactoring-sitrep.md +0 -64
- package/docs/ai/tkt-001-fix-database-error.md +0 -217
- package/docs/ai/ui-enhancement-plan.md +0 -562
- package/test/integration/app.isolated.tsx +0 -240
- package/test/integration/cli-commands.test.ts +0 -287
- package/test/integration/cli-validation.test.ts +0 -264
- package/test/integration/git-operations.test.ts +0 -218
- package/test/integration/scanner.test.ts +0 -228
- package/test/preload.ts +0 -18
- package/test/unit/cli/commands.test.ts +0 -13
- package/test/unit/cli/formatters.test.ts +0 -1116
- package/test/unit/cli/github-commands.test.ts +0 -12
- package/test/unit/components/CloneDialog.test.tsx +0 -240
- package/test/unit/components/ColumnHeader.test.tsx +0 -128
- package/test/unit/components/CommandPalette.test.tsx +0 -355
- package/test/unit/components/ConfirmDialog.test.tsx +0 -111
- package/test/unit/components/ErrorBoundary.test.tsx +0 -139
- package/test/unit/components/FilterBar.test.tsx +0 -43
- package/test/unit/components/FilterOptionsOverlay.test.tsx +0 -197
- package/test/unit/components/HelpOverlay.test.tsx +0 -90
- package/test/unit/components/Layout.test.tsx +0 -328
- package/test/unit/components/MarkdownRenderer.test.tsx +0 -45
- package/test/unit/components/ProgressBar.test.tsx +0 -138
- package/test/unit/components/ProjectItem.test.tsx +0 -182
- package/test/unit/components/ProjectList.test.tsx +0 -311
- package/test/unit/components/RepoDetailModal.test.tsx +0 -445
- package/test/unit/components/StatusBar.test.tsx +0 -112
- package/test/unit/components/UnifiedProjectItem.test.tsx +0 -618
- package/test/unit/components/ViewModeIndicator.test.tsx +0 -137
- package/test/unit/components/test-utils.tsx +0 -63
- package/test/unit/config/loader.test.ts +0 -692
- package/test/unit/db/database.test.ts +0 -978
- package/test/unit/db/index.test.ts +0 -314
- package/test/unit/fixtures/setup.ts +0 -186
- package/test/unit/git/commands-untested.test.ts +0 -205
- package/test/unit/git/commands.test.ts +0 -269
- package/test/unit/git/operations.test.ts +0 -322
- package/test/unit/git/status.test.ts +0 -219
- package/test/unit/github/auth.test.ts +0 -317
- package/test/unit/github/cache.test.ts +0 -1028
- package/test/unit/github/cli.test.ts +0 -135
- package/test/unit/github/unified.test.ts +0 -1201
- package/test/unit/graceful-shutdown.test.ts +0 -83
- package/test/unit/hooks/useBackgroundFetch.test.tsx +0 -239
- package/test/unit/hooks/useConfirmDialogActions.test.tsx +0 -81
- package/test/unit/hooks/useKeyBindings.isolated.ts +0 -715
- package/test/unit/hooks/useProjects.test.tsx +0 -186
- package/test/unit/hooks/useUnifiedRepos-simple.test.tsx +0 -115
- package/test/unit/hooks/useUnifiedRepos.test.tsx +0 -177
- package/test/unit/mocks/config.ts +0 -109
- package/test/unit/mocks/git-service.ts +0 -274
- package/test/unit/mocks/github-service.ts +0 -250
- package/test/unit/mocks/index.ts +0 -72
- package/test/unit/mocks/project.ts +0 -148
- package/test/unit/mocks/state-mocks.ts +0 -187
- package/test/unit/mocks/unified.ts +0 -169
- package/test/unit/operations/batch.test.ts +0 -216
- package/test/unit/operations/commands.test.ts +0 -550
- package/test/unit/scanner/errors.test.ts +0 -297
- package/test/unit/scanner/index.test.ts +0 -1011
- package/test/unit/scanner/markers.test.ts +0 -150
- package/test/unit/scanner/submodules.test.ts +0 -99
- package/test/unit/services/git-errors.test.ts +0 -190
- package/test/unit/services/git.test.ts +0 -442
- package/test/unit/services/github-errors.test.ts +0 -293
- package/test/unit/services/github.test.ts +0 -200
- package/test/unit/state/actions.test.ts +0 -217
- package/test/unit/state/reducer.test.ts +0 -745
- package/test/unit/state/store.test.tsx +0 -711
- package/test/unit/types/commands.test.ts +0 -220
- package/test/unit/types/schema.test.ts +0 -179
- package/test/unit/utils/array.test.ts +0 -73
- package/test/unit/utils/debug.test.ts +0 -23
- package/test/unit/utils/errors.test.ts +0 -295
- package/test/unit/utils/markdown.test.ts +0 -163
- package/test/unit/utils/project-utils.test.ts +0 -756
- package/test/unit/utils/rate-limiter.test.ts +0 -256
- package/test/unit/utils/retry.test.ts +0 -165
- package/test/unit/utils/strip-ansi.ts +0 -13
- package/test/unit/utils/timeout.test.ts +0 -93
- package/tsconfig.json +0 -29
|
@@ -1,314 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for database module functions
|
|
3
|
-
* Tests the actual exported functions from src/db/index.ts
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { describe, test, expect, beforeEach, afterEach } from "bun:test";
|
|
7
|
-
import { join } from "path";
|
|
8
|
-
import { existsSync, rmSync } from "fs";
|
|
9
|
-
import { eq } from "drizzle-orm";
|
|
10
|
-
import * as dbModule from "../../../src/db/index.ts";
|
|
11
|
-
|
|
12
|
-
describe("Database Module Functions", () => {
|
|
13
|
-
let originalEnv: Record<string, string | undefined>;
|
|
14
|
-
let tempDir: string;
|
|
15
|
-
|
|
16
|
-
beforeEach(() => {
|
|
17
|
-
// Save original environment
|
|
18
|
-
originalEnv = { ...process.env };
|
|
19
|
-
|
|
20
|
-
// Create a temporary directory for test databases
|
|
21
|
-
tempDir = `/tmp/gitforest-test-${Date.now()}`;
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
afterEach(() => {
|
|
25
|
-
// Restore original environment
|
|
26
|
-
process.env = originalEnv;
|
|
27
|
-
|
|
28
|
-
// Clean up temp directory
|
|
29
|
-
if (existsSync(tempDir)) {
|
|
30
|
-
rmSync(tempDir, { recursive: true, force: true });
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// Close any open database connections
|
|
34
|
-
try {
|
|
35
|
-
dbModule.closeDb();
|
|
36
|
-
} catch {
|
|
37
|
-
// Ignore errors during cleanup
|
|
38
|
-
}
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
describe("getDbPath", () => {
|
|
42
|
-
test("returns correct path with XDG_DATA_HOME set", () => {
|
|
43
|
-
process.env.XDG_DATA_HOME = "/custom/data/home";
|
|
44
|
-
|
|
45
|
-
const result = dbModule.getDbPath();
|
|
46
|
-
|
|
47
|
-
expect(result).toBe(join("/custom/data/home", "gitforest", "cache.db"));
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
test("returns default path without XDG_DATA_HOME", () => {
|
|
51
|
-
// Remove XDG_DATA_HOME if it exists
|
|
52
|
-
delete process.env.XDG_DATA_HOME;
|
|
53
|
-
|
|
54
|
-
const result = dbModule.getDbPath();
|
|
55
|
-
|
|
56
|
-
// Should use homedir() + .local/share/gitforest/cache.db
|
|
57
|
-
expect(result).toContain(".local");
|
|
58
|
-
expect(result).toContain("share");
|
|
59
|
-
expect(result).toContain("gitforest");
|
|
60
|
-
expect(result).toContain("cache.db");
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
test("constructs path correctly on different platforms", () => {
|
|
64
|
-
// This test verifies the path construction logic
|
|
65
|
-
// The actual homedir() value depends on the test environment
|
|
66
|
-
delete process.env.XDG_DATA_HOME;
|
|
67
|
-
|
|
68
|
-
const result = dbModule.getDbPath();
|
|
69
|
-
|
|
70
|
-
expect(result).toMatch(/.*gitforest.*cache\.db$/);
|
|
71
|
-
expect(result).not.toContain("undefined");
|
|
72
|
-
});
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
describe("initDb", () => {
|
|
76
|
-
test("creates database file and directory", async () => {
|
|
77
|
-
// Set a custom temp directory for the database
|
|
78
|
-
process.env.XDG_DATA_HOME = tempDir;
|
|
79
|
-
|
|
80
|
-
const db = await dbModule.initDb();
|
|
81
|
-
|
|
82
|
-
expect(db).toBeDefined();
|
|
83
|
-
|
|
84
|
-
// Verify database file was created
|
|
85
|
-
const dbPath = dbModule.getDbPath();
|
|
86
|
-
expect(existsSync(dbPath)).toBe(true);
|
|
87
|
-
|
|
88
|
-
// Verify directory was created
|
|
89
|
-
const dbDir = join(dbPath, "..");
|
|
90
|
-
expect(existsSync(dbDir)).toBe(true);
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
test("enables WAL mode", async () => {
|
|
94
|
-
process.env.XDG_DATA_HOME = tempDir;
|
|
95
|
-
|
|
96
|
-
await dbModule.initDb();
|
|
97
|
-
|
|
98
|
-
// WAL mode is enabled via PRAGMA, we can't easily test it directly
|
|
99
|
-
// but we can verify the database was created successfully
|
|
100
|
-
const dbPath = dbModule.getDbPath();
|
|
101
|
-
expect(existsSync(dbPath)).toBe(true);
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
test("creates all required tables", async () => {
|
|
105
|
-
process.env.XDG_DATA_HOME = tempDir;
|
|
106
|
-
|
|
107
|
-
const db = await dbModule.initDb();
|
|
108
|
-
|
|
109
|
-
// Try to query each table to verify they exist
|
|
110
|
-
const tables = [
|
|
111
|
-
"projects",
|
|
112
|
-
"remote_status",
|
|
113
|
-
"github_repos",
|
|
114
|
-
"config_cache"
|
|
115
|
-
];
|
|
116
|
-
|
|
117
|
-
for (const table of tables) {
|
|
118
|
-
// This should not throw if table exists
|
|
119
|
-
const result = db.run(`SELECT 1 FROM ${table} LIMIT 1`);
|
|
120
|
-
expect(result).toBeDefined();
|
|
121
|
-
}
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
test("returns same instance on repeated calls (singleton)", async () => {
|
|
125
|
-
process.env.XDG_DATA_HOME = tempDir;
|
|
126
|
-
|
|
127
|
-
const db1 = await dbModule.initDb();
|
|
128
|
-
const db2 = await dbModule.initDb();
|
|
129
|
-
|
|
130
|
-
expect(db1).toBe(db2);
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
test("handles directory creation errors", async () => {
|
|
134
|
-
// Create a file where we want to create a directory
|
|
135
|
-
const conflictingPath = join(tempDir, "gitforest");
|
|
136
|
-
process.env.XDG_DATA_HOME = tempDir;
|
|
137
|
-
|
|
138
|
-
// Create a file with the same name as the directory we want
|
|
139
|
-
await Bun.write(conflictingPath, "conflict");
|
|
140
|
-
|
|
141
|
-
// This should throw an error because mkdir -p will fail
|
|
142
|
-
await expect(dbModule.initDb()).rejects.toThrow();
|
|
143
|
-
});
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
describe("closeDb", () => {
|
|
147
|
-
test("closes database connection", async () => {
|
|
148
|
-
process.env.XDG_DATA_HOME = tempDir;
|
|
149
|
-
|
|
150
|
-
await dbModule.initDb();
|
|
151
|
-
dbModule.closeDb();
|
|
152
|
-
|
|
153
|
-
// After closing, getDb() should throw an error
|
|
154
|
-
expect(() => dbModule.getDb()).toThrow("Database not initialized");
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
test("resets db and sqliteDb to null", async () => {
|
|
158
|
-
process.env.XDG_DATA_HOME = tempDir;
|
|
159
|
-
|
|
160
|
-
await dbModule.initDb();
|
|
161
|
-
dbModule.closeDb();
|
|
162
|
-
|
|
163
|
-
// Initialize again to verify it works after closing
|
|
164
|
-
const db = await dbModule.initDb();
|
|
165
|
-
expect(db).toBeDefined();
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
test("handles already closed database gracefully", async () => {
|
|
169
|
-
process.env.XDG_DATA_HOME = tempDir;
|
|
170
|
-
|
|
171
|
-
await dbModule.initDb();
|
|
172
|
-
dbModule.closeDb();
|
|
173
|
-
|
|
174
|
-
// Calling closeDb again should not throw
|
|
175
|
-
expect(() => dbModule.closeDb()).not.toThrow();
|
|
176
|
-
});
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
describe("getDb", () => {
|
|
180
|
-
test("throws error when database not initialized", () => {
|
|
181
|
-
expect(() => dbModule.getDb()).toThrow("Database not initialized. Call initDb() first.");
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
test("returns database instance after initDb", async () => {
|
|
185
|
-
process.env.XDG_DATA_HOME = tempDir;
|
|
186
|
-
|
|
187
|
-
await dbModule.initDb();
|
|
188
|
-
const db = dbModule.getDb();
|
|
189
|
-
|
|
190
|
-
expect(db).toBeDefined();
|
|
191
|
-
expect(typeof db.select).toBe("function");
|
|
192
|
-
expect(typeof db.insert).toBe("function");
|
|
193
|
-
expect(typeof db.update).toBe("function");
|
|
194
|
-
expect(typeof db.delete).toBe("function");
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
test("returns same instance on multiple calls", async () => {
|
|
198
|
-
process.env.XDG_DATA_HOME = tempDir;
|
|
199
|
-
|
|
200
|
-
await dbModule.initDb();
|
|
201
|
-
const db1 = dbModule.getDb();
|
|
202
|
-
const db2 = dbModule.getDb();
|
|
203
|
-
|
|
204
|
-
expect(db1).toBe(db2);
|
|
205
|
-
});
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
describe("clearCache", () => {
|
|
209
|
-
test("deletes all projects", async () => {
|
|
210
|
-
process.env.XDG_DATA_HOME = tempDir;
|
|
211
|
-
|
|
212
|
-
const db = await dbModule.initDb();
|
|
213
|
-
|
|
214
|
-
// Insert some test data
|
|
215
|
-
await db.insert(dbModule.schema.projects).values([
|
|
216
|
-
{ id: "test-1", name: "Test 1", path: "/test/1", type: "git" },
|
|
217
|
-
{ id: "test-2", name: "Test 2", path: "/test/2", type: "git" },
|
|
218
|
-
]).run();
|
|
219
|
-
|
|
220
|
-
// Verify data exists
|
|
221
|
-
let projects = await db.select().from(dbModule.schema.projects).all();
|
|
222
|
-
expect(projects).toHaveLength(2);
|
|
223
|
-
|
|
224
|
-
// Clear cache
|
|
225
|
-
await dbModule.clearCache();
|
|
226
|
-
|
|
227
|
-
// Verify projects are deleted
|
|
228
|
-
projects = await db.select().from(dbModule.schema.projects).all();
|
|
229
|
-
expect(projects).toHaveLength(0);
|
|
230
|
-
});
|
|
231
|
-
|
|
232
|
-
test("deletes all remoteStatus entries", async () => {
|
|
233
|
-
process.env.XDG_DATA_HOME = tempDir;
|
|
234
|
-
|
|
235
|
-
const db = await dbModule.initDb();
|
|
236
|
-
|
|
237
|
-
// Insert a project first (foreign key constraint)
|
|
238
|
-
await db.insert(dbModule.schema.projects).values({
|
|
239
|
-
id: "test-1",
|
|
240
|
-
name: "Test 1",
|
|
241
|
-
path: "/test/1",
|
|
242
|
-
type: "git",
|
|
243
|
-
}).run();
|
|
244
|
-
|
|
245
|
-
// Insert remote status
|
|
246
|
-
await db.insert(dbModule.schema.remoteStatus).values({
|
|
247
|
-
projectId: "test-1",
|
|
248
|
-
unpulledCommits: 5,
|
|
249
|
-
}).run();
|
|
250
|
-
|
|
251
|
-
// Verify data exists
|
|
252
|
-
let remoteStatus = await db.select().from(dbModule.schema.remoteStatus).all();
|
|
253
|
-
expect(remoteStatus).toHaveLength(1);
|
|
254
|
-
|
|
255
|
-
// Clear cache
|
|
256
|
-
await dbModule.clearCache();
|
|
257
|
-
|
|
258
|
-
// Verify remote status is deleted
|
|
259
|
-
remoteStatus = await db.select().from(dbModule.schema.remoteStatus).all();
|
|
260
|
-
expect(remoteStatus).toHaveLength(0);
|
|
261
|
-
});
|
|
262
|
-
|
|
263
|
-
test("clears cache successfully", async () => {
|
|
264
|
-
process.env.XDG_DATA_HOME = tempDir;
|
|
265
|
-
|
|
266
|
-
const db = await dbModule.initDb();
|
|
267
|
-
|
|
268
|
-
// Insert some test data
|
|
269
|
-
await db.insert(dbModule.schema.projects).values({
|
|
270
|
-
id: "clear-test",
|
|
271
|
-
name: "Clear Test",
|
|
272
|
-
path: "/clear/test",
|
|
273
|
-
type: "git",
|
|
274
|
-
}).run();
|
|
275
|
-
|
|
276
|
-
// Verify data exists
|
|
277
|
-
let projects = await db.select().from(dbModule.schema.projects).all();
|
|
278
|
-
expect(projects).toHaveLength(1);
|
|
279
|
-
|
|
280
|
-
// Clear cache
|
|
281
|
-
await dbModule.clearCache();
|
|
282
|
-
|
|
283
|
-
// Verify projects are deleted
|
|
284
|
-
projects = await db.select().from(dbModule.schema.projects).all();
|
|
285
|
-
expect(projects).toHaveLength(0);
|
|
286
|
-
});
|
|
287
|
-
});
|
|
288
|
-
|
|
289
|
-
describe("Integration with existing schema tests", () => {
|
|
290
|
-
test("works with existing database operations", async () => {
|
|
291
|
-
process.env.XDG_DATA_HOME = tempDir;
|
|
292
|
-
|
|
293
|
-
const db = await dbModule.initDb();
|
|
294
|
-
|
|
295
|
-
// Test that we can use the database with the schema
|
|
296
|
-
const project = {
|
|
297
|
-
id: "integration-test",
|
|
298
|
-
name: "Integration Test",
|
|
299
|
-
path: "/integration/test",
|
|
300
|
-
type: "git" as const,
|
|
301
|
-
};
|
|
302
|
-
|
|
303
|
-
await db.insert(dbModule.schema.projects).values(project).run();
|
|
304
|
-
|
|
305
|
-
const result = await db.select()
|
|
306
|
-
.from(dbModule.schema.projects)
|
|
307
|
-
.where(eq(dbModule.schema.projects.id, "integration-test"))
|
|
308
|
-
.get();
|
|
309
|
-
|
|
310
|
-
expect(result).toBeDefined();
|
|
311
|
-
expect(result!.name).toBe("Integration Test");
|
|
312
|
-
});
|
|
313
|
-
});
|
|
314
|
-
});
|
|
@@ -1,186 +0,0 @@
|
|
|
1
|
-
import { mkdirSync, rmSync, writeFileSync, realpathSync } from "fs";
|
|
2
|
-
import { join } from "path";
|
|
3
|
-
import { tmpdir } from "os";
|
|
4
|
-
import { $ } from "bun";
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Create a temporary directory for testing
|
|
8
|
-
* Uses system temp directory to avoid being inside gitforest's git repo
|
|
9
|
-
* Returns resolved real path to avoid macOS /var -> /private/var issues
|
|
10
|
-
*/
|
|
11
|
-
export function createTempDir(name: string): string {
|
|
12
|
-
const tmpDir = join(tmpdir(), "gitforest-test", name);
|
|
13
|
-
rmSync(tmpDir, { recursive: true, force: true });
|
|
14
|
-
mkdirSync(tmpDir, { recursive: true });
|
|
15
|
-
// Return real path to handle macOS symlinks (/var -> /private/var)
|
|
16
|
-
return realpathSync(tmpDir);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Clean up a temporary directory
|
|
21
|
-
*/
|
|
22
|
-
export function cleanupTempDir(path: string): void {
|
|
23
|
-
rmSync(path, { recursive: true, force: true });
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Create a mock git repository
|
|
28
|
-
*/
|
|
29
|
-
export async function createMockGitRepo(
|
|
30
|
-
basePath: string,
|
|
31
|
-
name: string,
|
|
32
|
-
options?: {
|
|
33
|
-
withRemote?: boolean;
|
|
34
|
-
withChanges?: boolean;
|
|
35
|
-
withUntracked?: boolean;
|
|
36
|
-
withStaged?: boolean;
|
|
37
|
-
commitCount?: number;
|
|
38
|
-
}
|
|
39
|
-
): Promise<string> {
|
|
40
|
-
// Ensure the base path exists first
|
|
41
|
-
mkdirSync(basePath, { recursive: true });
|
|
42
|
-
|
|
43
|
-
// Use realpath for consistent paths on macOS
|
|
44
|
-
const baseReal = realpathSync(basePath);
|
|
45
|
-
const repoPath = join(baseReal, name);
|
|
46
|
-
mkdirSync(repoPath, { recursive: true });
|
|
47
|
-
|
|
48
|
-
// Initialize git repo
|
|
49
|
-
await $`git -C ${repoPath} init`.quiet();
|
|
50
|
-
await $`git -C ${repoPath} config user.email "test@test.com"`.quiet();
|
|
51
|
-
await $`git -C ${repoPath} config user.name "Test User"`.quiet();
|
|
52
|
-
// Disable hooks and GPG signing to ensure reliable testing
|
|
53
|
-
await $`git -C ${repoPath} config core.hooksPath /dev/null`.quiet();
|
|
54
|
-
await $`git -C ${repoPath} config commit.gpgsign false`.quiet();
|
|
55
|
-
|
|
56
|
-
// Create initial commit
|
|
57
|
-
writeFileSync(join(repoPath, "README.md"), `# ${name}\n`);
|
|
58
|
-
await $`git -C ${repoPath} add README.md`.quiet();
|
|
59
|
-
await $`git -C ${repoPath} commit -m "Initial commit"`.quiet();
|
|
60
|
-
|
|
61
|
-
// Add additional commits if requested
|
|
62
|
-
const commitCount = options?.commitCount ?? 0;
|
|
63
|
-
for (let i = 0; i < commitCount; i++) {
|
|
64
|
-
writeFileSync(join(repoPath, `file${i}.txt`), `Content ${i}\n`);
|
|
65
|
-
await $`git -C ${repoPath} add file${i}.txt`.quiet();
|
|
66
|
-
await $`git -C ${repoPath} commit -m "Commit ${i + 1}"`.quiet();
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// Add unstaged changes
|
|
70
|
-
if (options?.withChanges) {
|
|
71
|
-
writeFileSync(join(repoPath, "README.md"), `# ${name}\n\nModified\n`);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// Add untracked files
|
|
75
|
-
if (options?.withUntracked) {
|
|
76
|
-
writeFileSync(join(repoPath, "untracked.txt"), "Untracked content\n");
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// Add staged changes
|
|
80
|
-
if (options?.withStaged) {
|
|
81
|
-
writeFileSync(join(repoPath, "staged.txt"), "Staged content\n");
|
|
82
|
-
await $`git -C ${repoPath} add staged.txt`.quiet();
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
return repoPath;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Create a mock non-git project with a marker file
|
|
90
|
-
*/
|
|
91
|
-
export function createMockProject(
|
|
92
|
-
basePath: string,
|
|
93
|
-
name: string,
|
|
94
|
-
marker: string
|
|
95
|
-
): string {
|
|
96
|
-
// Ensure the base path exists first
|
|
97
|
-
mkdirSync(basePath, { recursive: true });
|
|
98
|
-
|
|
99
|
-
// Use realpath for consistent paths on macOS
|
|
100
|
-
const baseReal = realpathSync(basePath);
|
|
101
|
-
const projectPath = join(baseReal, name);
|
|
102
|
-
mkdirSync(projectPath, { recursive: true });
|
|
103
|
-
|
|
104
|
-
// Create marker file based on type
|
|
105
|
-
switch (marker) {
|
|
106
|
-
case "package.json":
|
|
107
|
-
writeFileSync(
|
|
108
|
-
join(projectPath, "package.json"),
|
|
109
|
-
JSON.stringify({ name, version: "1.0.0" }, null, 2)
|
|
110
|
-
);
|
|
111
|
-
break;
|
|
112
|
-
case "Cargo.toml":
|
|
113
|
-
writeFileSync(
|
|
114
|
-
join(projectPath, "Cargo.toml"),
|
|
115
|
-
`[package]\nname = "${name}"\nversion = "0.1.0"\n`
|
|
116
|
-
);
|
|
117
|
-
break;
|
|
118
|
-
case "pyproject.toml":
|
|
119
|
-
writeFileSync(
|
|
120
|
-
join(projectPath, "pyproject.toml"),
|
|
121
|
-
`[project]\nname = "${name}"\nversion = "0.1.0"\n`
|
|
122
|
-
);
|
|
123
|
-
break;
|
|
124
|
-
case "go.mod":
|
|
125
|
-
writeFileSync(join(projectPath, "go.mod"), `module ${name}\n\ngo 1.21\n`);
|
|
126
|
-
break;
|
|
127
|
-
default:
|
|
128
|
-
writeFileSync(join(projectPath, marker), "");
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
return projectPath;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* Create a mock git repo with submodule
|
|
136
|
-
*/
|
|
137
|
-
export async function createMockRepoWithSubmodule(
|
|
138
|
-
basePath: string,
|
|
139
|
-
parentName: string,
|
|
140
|
-
submoduleName: string
|
|
141
|
-
): Promise<{ parentPath: string; submodulePath: string }> {
|
|
142
|
-
// Ensure the base path exists first
|
|
143
|
-
mkdirSync(basePath, { recursive: true });
|
|
144
|
-
|
|
145
|
-
// Create the submodule repo first (use realpath to ensure consistent paths)
|
|
146
|
-
const submoduleSource = await createMockGitRepo(basePath, `${submoduleName}-source`);
|
|
147
|
-
const submoduleSourceReal = realpathSync(submoduleSource);
|
|
148
|
-
|
|
149
|
-
// Create parent repo
|
|
150
|
-
const parentPath = await createMockGitRepo(basePath, parentName);
|
|
151
|
-
const parentPathReal = realpathSync(parentPath);
|
|
152
|
-
|
|
153
|
-
// Allow file:// protocol for submodule add (needed for local testing)
|
|
154
|
-
// Set in both the parent repo and globally for the submodule clone operation
|
|
155
|
-
await $`git -C ${parentPathReal} config --local protocol.file.allow always`.quiet();
|
|
156
|
-
await $`git config --global protocol.file.allow always`.quiet();
|
|
157
|
-
|
|
158
|
-
// Add submodule using real paths
|
|
159
|
-
await $`git -C ${parentPathReal} submodule add ${submoduleSourceReal} ${submoduleName}`.quiet();
|
|
160
|
-
await $`git -C ${parentPathReal} commit -m "Add submodule"`.quiet();
|
|
161
|
-
|
|
162
|
-
const submodulePath = join(parentPathReal, submoduleName);
|
|
163
|
-
|
|
164
|
-
return { parentPath: parentPathReal, submodulePath };
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
/**
|
|
168
|
-
* Create a mock config file
|
|
169
|
-
*/
|
|
170
|
-
export function createMockConfig(
|
|
171
|
-
basePath: string,
|
|
172
|
-
config: Record<string, unknown>
|
|
173
|
-
): string {
|
|
174
|
-
const configPath = join(basePath, "gitforest.config.json");
|
|
175
|
-
writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
176
|
-
return configPath;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
/**
|
|
180
|
-
* Create a mock YAML config file
|
|
181
|
-
*/
|
|
182
|
-
export function createMockYamlConfig(basePath: string, content: string): string {
|
|
183
|
-
const configPath = join(basePath, "gitforest.config.yaml");
|
|
184
|
-
writeFileSync(configPath, content);
|
|
185
|
-
return configPath;
|
|
186
|
-
}
|
|
@@ -1,205 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for untested functions in git/commands.ts
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { describe, test, expect, beforeEach, afterEach } from "bun:test";
|
|
6
|
-
import {
|
|
7
|
-
getTrackingBranch,
|
|
8
|
-
countUnpushedCommits,
|
|
9
|
-
countUnpulledCommits,
|
|
10
|
-
getRemoteLastCommitDate,
|
|
11
|
-
addRemote,
|
|
12
|
-
fetchRemote,
|
|
13
|
-
fetchAll,
|
|
14
|
-
pull,
|
|
15
|
-
push,
|
|
16
|
-
getSubmoduleParent,
|
|
17
|
-
hasCommits,
|
|
18
|
-
} from "../../../src/git/commands.ts";
|
|
19
|
-
import {
|
|
20
|
-
createTempDir,
|
|
21
|
-
cleanupTempDir,
|
|
22
|
-
createMockGitRepo,
|
|
23
|
-
createMockRepoWithSubmodule,
|
|
24
|
-
} from "../fixtures/setup.ts";
|
|
25
|
-
import { mkdir, writeFile } from "fs/promises";
|
|
26
|
-
import { join } from "path";
|
|
27
|
-
import { $ } from "bun";
|
|
28
|
-
|
|
29
|
-
describe("Git Commands - Untested Functions", () => {
|
|
30
|
-
let tempDir: string;
|
|
31
|
-
|
|
32
|
-
beforeEach(() => {
|
|
33
|
-
tempDir = createTempDir("git-commands-untested");
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
afterEach(() => {
|
|
37
|
-
cleanupTempDir(tempDir);
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
describe("getTrackingBranch", () => {
|
|
41
|
-
test("returns null when no tracking branch", async () => {
|
|
42
|
-
const repoPath = await createMockGitRepo(tempDir, "no-tracking");
|
|
43
|
-
|
|
44
|
-
const result = await getTrackingBranch(repoPath);
|
|
45
|
-
expect(result).toBeNull();
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
test("returns null for non-git directory", async () => {
|
|
49
|
-
const nonGitPath = join(tempDir, "non-git");
|
|
50
|
-
await mkdir(nonGitPath, { recursive: true });
|
|
51
|
-
|
|
52
|
-
const result = await getTrackingBranch(nonGitPath);
|
|
53
|
-
expect(result).toBeNull();
|
|
54
|
-
});
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
describe("countUnpushedCommits", () => {
|
|
58
|
-
test("returns 0 when no tracking branch", async () => {
|
|
59
|
-
const repoPath = await createMockGitRepo(tempDir, "no-tracking");
|
|
60
|
-
|
|
61
|
-
const result = await countUnpushedCommits(repoPath);
|
|
62
|
-
expect(result).toBe(0);
|
|
63
|
-
});
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
describe("countUnpulledCommits", () => {
|
|
67
|
-
test("returns 0 when no unpulled commits", async () => {
|
|
68
|
-
const repoPath = await createMockGitRepo(tempDir, "no-unpulled");
|
|
69
|
-
|
|
70
|
-
const result = await countUnpulledCommits(repoPath);
|
|
71
|
-
expect(result).toBe(0);
|
|
72
|
-
});
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
describe("getRemoteLastCommitDate", () => {
|
|
76
|
-
test("returns null for non-existent remote branch", async () => {
|
|
77
|
-
const repoPath = await createMockGitRepo(tempDir, "no-remote-branch");
|
|
78
|
-
|
|
79
|
-
const result = await getRemoteLastCommitDate(repoPath, "origin/nonexistent");
|
|
80
|
-
expect(result).toBeNull();
|
|
81
|
-
});
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
describe("addRemote", () => {
|
|
85
|
-
test("adds remote with default name", async () => {
|
|
86
|
-
const repoPath = await createMockGitRepo(tempDir, "add-remote");
|
|
87
|
-
|
|
88
|
-
const result = await addRemote(repoPath, "https://github.com/test/repo.git");
|
|
89
|
-
|
|
90
|
-
expect(result).toBe(true);
|
|
91
|
-
|
|
92
|
-
// Verify remote was added
|
|
93
|
-
const remoteUrl = await $`git -C ${repoPath} config --get remote.origin.url`.text();
|
|
94
|
-
expect(remoteUrl.trim()).toBe("https://github.com/test/repo.git");
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
test("adds remote with custom name", async () => {
|
|
98
|
-
const repoPath = await createMockGitRepo(tempDir, "add-custom-remote");
|
|
99
|
-
|
|
100
|
-
const result = await addRemote(repoPath, "https://github.com/test/repo.git", "upstream");
|
|
101
|
-
|
|
102
|
-
expect(result).toBe(true);
|
|
103
|
-
|
|
104
|
-
// Verify remote was added with custom name
|
|
105
|
-
const remoteUrl = await $`git -C ${repoPath} config --get remote.upstream.url`.text();
|
|
106
|
-
expect(remoteUrl.trim()).toBe("https://github.com/test/repo.git");
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
test("returns false when remote already exists", async () => {
|
|
110
|
-
const repoPath = await createMockGitRepo(tempDir, "existing-remote");
|
|
111
|
-
|
|
112
|
-
// Add remote first
|
|
113
|
-
await addRemote(repoPath, "https://github.com/test/repo.git", "origin");
|
|
114
|
-
|
|
115
|
-
// Try to add again
|
|
116
|
-
const result = await addRemote(repoPath, "https://github.com/test/repo2.git", "origin");
|
|
117
|
-
|
|
118
|
-
expect(result).toBe(false);
|
|
119
|
-
});
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
describe("fetchRemote", () => {
|
|
123
|
-
test("returns false when remote doesn't exist", async () => {
|
|
124
|
-
const repoPath = await createMockGitRepo(tempDir, "no-remote-fetch");
|
|
125
|
-
|
|
126
|
-
const result = await fetchRemote(repoPath, "nonexistent");
|
|
127
|
-
expect(result).toBe(false);
|
|
128
|
-
});
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
describe("fetchAll", () => {
|
|
132
|
-
test("handles repos with no remotes", async () => {
|
|
133
|
-
const repoPath = await createMockGitRepo(tempDir, "no-remotes");
|
|
134
|
-
|
|
135
|
-
const result = await fetchAll(repoPath);
|
|
136
|
-
expect(result).toBe(true);
|
|
137
|
-
});
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
describe("pull", () => {
|
|
141
|
-
test("returns false on merge conflicts", async () => {
|
|
142
|
-
const repoPath = await createMockGitRepo(tempDir, "conflict-repo");
|
|
143
|
-
|
|
144
|
-
// Create a conflicting change
|
|
145
|
-
await writeFile(join(repoPath, "README.md"), "# Modified");
|
|
146
|
-
await $`git -C ${repoPath} add README.md`.quiet();
|
|
147
|
-
await $`git -C ${repoPath} commit -m "Local change"`.quiet();
|
|
148
|
-
|
|
149
|
-
// Try to pull (will fail due to no remote)
|
|
150
|
-
const result = await pull(repoPath);
|
|
151
|
-
expect(result).toBe(false);
|
|
152
|
-
});
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
describe("push", () => {
|
|
156
|
-
test("returns false when no remote", async () => {
|
|
157
|
-
const repoPath = await createMockGitRepo(tempDir, "push-no-remote");
|
|
158
|
-
|
|
159
|
-
const result = await push(repoPath, false);
|
|
160
|
-
expect(result).toBe(false);
|
|
161
|
-
});
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
describe("getSubmoduleParent", () => {
|
|
165
|
-
test("returns parent path for submodule", async () => {
|
|
166
|
-
const { submodulePath } = await createMockRepoWithSubmodule(
|
|
167
|
-
tempDir,
|
|
168
|
-
"parent",
|
|
169
|
-
"submodule"
|
|
170
|
-
);
|
|
171
|
-
|
|
172
|
-
const result = await getSubmoduleParent(submodulePath);
|
|
173
|
-
expect(result).toBeTruthy();
|
|
174
|
-
expect(result).toContain("parent");
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
test("returns null for regular repo", async () => {
|
|
178
|
-
const repoPath = await createMockGitRepo(tempDir, "regular-repo");
|
|
179
|
-
|
|
180
|
-
const result = await getSubmoduleParent(repoPath);
|
|
181
|
-
expect(result).toBeNull();
|
|
182
|
-
});
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
describe("hasCommits", () => {
|
|
186
|
-
test("returns true for repo with commits", async () => {
|
|
187
|
-
const repoPath = await createMockGitRepo(tempDir, "with-commits");
|
|
188
|
-
|
|
189
|
-
const result = await hasCommits(repoPath);
|
|
190
|
-
expect(result).toBe(true);
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
test("returns false for empty repo", async () => {
|
|
194
|
-
// Create an empty repo without initial commit
|
|
195
|
-
const emptyRepoPath = join(tempDir, "empty-repo");
|
|
196
|
-
await mkdir(emptyRepoPath, { recursive: true });
|
|
197
|
-
await $`git -C ${emptyRepoPath} init`.quiet();
|
|
198
|
-
await $`git -C ${emptyRepoPath} config user.email "test@test.com"`.quiet();
|
|
199
|
-
await $`git -C ${emptyRepoPath} config user.name "Test"`.quiet();
|
|
200
|
-
|
|
201
|
-
const result = await hasCommits(emptyRepoPath);
|
|
202
|
-
expect(result).toBe(false);
|
|
203
|
-
});
|
|
204
|
-
});
|
|
205
|
-
});
|