chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
import { expect } from 'vitest'
|
||||
|
||||
import { sandboxTest } from '../../setup.js'
|
||||
import { cleanupBaseDir, createBaseDir, createRepo } from './helpers.js'
|
||||
|
||||
sandboxTest('git add stages files', async ({ sandbox }) => {
|
||||
const baseDir = await createBaseDir(sandbox)
|
||||
|
||||
try {
|
||||
const repoPath = await createRepo(sandbox, baseDir)
|
||||
await sandbox.files.write(`${repoPath}/README.md`, 'hello\n')
|
||||
|
||||
await sandbox.git.add(repoPath)
|
||||
|
||||
const status = await sandbox.git.status(repoPath)
|
||||
const entry = status.fileStatus.find(
|
||||
(file: any) => file.name === 'README.md'
|
||||
)
|
||||
expect(entry?.status).toBe('added')
|
||||
expect(entry?.staged).toBe(true)
|
||||
} finally {
|
||||
await cleanupBaseDir(sandbox, baseDir)
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,82 @@
|
||||
import { expect } from 'vitest'
|
||||
|
||||
import { sandboxTest } from '../../setup.js'
|
||||
import {
|
||||
cleanupBaseDir,
|
||||
createBaseDir,
|
||||
createRepoWithCommit,
|
||||
} from './helpers.js'
|
||||
|
||||
sandboxTest('git branches lists current and feature', async ({ sandbox }) => {
|
||||
const baseDir = await createBaseDir(sandbox)
|
||||
|
||||
try {
|
||||
const repoPath = await createRepoWithCommit(sandbox, baseDir)
|
||||
await sandbox.commands.run(`git -C "${repoPath}" branch feature`)
|
||||
|
||||
const branches = await sandbox.git.branches(repoPath)
|
||||
expect(branches.currentBranch).toBe('main')
|
||||
expect(branches.branches).toContain('main')
|
||||
expect(branches.branches).toContain('feature')
|
||||
} finally {
|
||||
await cleanupBaseDir(sandbox, baseDir)
|
||||
}
|
||||
})
|
||||
|
||||
sandboxTest('git checkoutBranch switches branch', async ({ sandbox }) => {
|
||||
const baseDir = await createBaseDir(sandbox)
|
||||
|
||||
try {
|
||||
const repoPath = await createRepoWithCommit(sandbox, baseDir)
|
||||
await sandbox.commands.run(`git -C "${repoPath}" branch feature`)
|
||||
|
||||
await sandbox.git.checkoutBranch(repoPath, 'feature')
|
||||
|
||||
const head = (
|
||||
await sandbox.commands.run(
|
||||
`git -C "${repoPath}" rev-parse --abbrev-ref HEAD`
|
||||
)
|
||||
).stdout.trim()
|
||||
expect(head).toBe('feature')
|
||||
} finally {
|
||||
await cleanupBaseDir(sandbox, baseDir)
|
||||
}
|
||||
})
|
||||
|
||||
sandboxTest(
|
||||
'git createBranch creates and checks out branch',
|
||||
async ({ sandbox }) => {
|
||||
const baseDir = await createBaseDir(sandbox)
|
||||
|
||||
try {
|
||||
const repoPath = await createRepoWithCommit(sandbox, baseDir)
|
||||
await sandbox.git.createBranch(repoPath, 'feature')
|
||||
|
||||
const branches = await sandbox.git.branches(repoPath)
|
||||
expect(branches.branches).toContain('feature')
|
||||
expect(branches.currentBranch).toBe('feature')
|
||||
} finally {
|
||||
await cleanupBaseDir(sandbox, baseDir)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
sandboxTest('git deleteBranch removes branch', async ({ sandbox }) => {
|
||||
const baseDir = await createBaseDir(sandbox)
|
||||
|
||||
try {
|
||||
const repoPath = await createRepoWithCommit(sandbox, baseDir)
|
||||
await sandbox.commands.run(`git -C "${repoPath}" branch feature`)
|
||||
|
||||
await sandbox.git.deleteBranch(repoPath, 'feature')
|
||||
|
||||
const branch = (
|
||||
await sandbox.commands.run(`git -C "${repoPath}" branch --list feature`)
|
||||
).stdout.trim()
|
||||
const branches = await sandbox.git.branches(repoPath)
|
||||
expect(branch).toBe('')
|
||||
expect(branches.branches).not.toContain('feature')
|
||||
} finally {
|
||||
await cleanupBaseDir(sandbox, baseDir)
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,35 @@
|
||||
import { expect } from 'vitest'
|
||||
|
||||
import { sandboxTest } from '../../setup.js'
|
||||
import {
|
||||
cleanupBaseDir,
|
||||
createBaseDir,
|
||||
createRepoWithCommit,
|
||||
startGitDaemon,
|
||||
} from './helpers.js'
|
||||
|
||||
sandboxTest('git clone fetches repo', async ({ sandbox }) => {
|
||||
const baseDir = await createBaseDir(sandbox)
|
||||
|
||||
try {
|
||||
const repoPath = await createRepoWithCommit(sandbox, baseDir)
|
||||
const daemon = await startGitDaemon(sandbox, baseDir)
|
||||
const clonePath = `${baseDir}/clone`
|
||||
|
||||
try {
|
||||
await sandbox.git.remoteAdd(repoPath, 'origin', daemon.remoteUrl)
|
||||
await sandbox.git.push(repoPath, {
|
||||
remote: 'origin',
|
||||
branch: 'main',
|
||||
})
|
||||
|
||||
await sandbox.git.clone(daemon.remoteUrl, { path: clonePath })
|
||||
const contents = await sandbox.files.read(`${clonePath}/README.md`)
|
||||
expect(contents).toContain('hello')
|
||||
} finally {
|
||||
await daemon.handle.kill()
|
||||
}
|
||||
} finally {
|
||||
await cleanupBaseDir(sandbox, baseDir)
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,66 @@
|
||||
import { expect } from 'vitest'
|
||||
|
||||
import { sandboxTest } from '../../setup.js'
|
||||
import {
|
||||
AUTHOR_EMAIL,
|
||||
AUTHOR_NAME,
|
||||
cleanupBaseDir,
|
||||
createBaseDir,
|
||||
createRepo,
|
||||
} from './helpers.js'
|
||||
|
||||
sandboxTest('git commit creates commit', async ({ sandbox }) => {
|
||||
const baseDir = await createBaseDir(sandbox)
|
||||
|
||||
try {
|
||||
const repoPath = await createRepo(sandbox, baseDir)
|
||||
await sandbox.files.write(`${repoPath}/README.md`, 'hello\n')
|
||||
await sandbox.git.add(repoPath)
|
||||
|
||||
await sandbox.git.commit(repoPath, 'Initial commit', {
|
||||
authorName: AUTHOR_NAME,
|
||||
authorEmail: AUTHOR_EMAIL,
|
||||
})
|
||||
|
||||
const message = (
|
||||
await sandbox.commands.run(`git -C "${repoPath}" log -1 --pretty=%B`)
|
||||
).stdout.trim()
|
||||
expect(message).toBe('Initial commit')
|
||||
} finally {
|
||||
await cleanupBaseDir(sandbox, baseDir)
|
||||
}
|
||||
})
|
||||
|
||||
sandboxTest(
|
||||
'git commit uses config for missing author fields',
|
||||
async ({ sandbox }) => {
|
||||
const baseDir = await createBaseDir(sandbox)
|
||||
|
||||
try {
|
||||
const repoPath = await createRepo(sandbox, baseDir)
|
||||
await sandbox.commands.run(
|
||||
`git -C "${repoPath}" config --local user.email "${AUTHOR_EMAIL}"`
|
||||
)
|
||||
|
||||
await sandbox.files.write(`${repoPath}/README.md`, 'hello\n')
|
||||
await sandbox.git.add(repoPath)
|
||||
|
||||
const overrideName = 'Override Bot'
|
||||
await sandbox.git.commit(repoPath, 'Partial author commit', {
|
||||
authorName: overrideName,
|
||||
})
|
||||
|
||||
const authorName = (
|
||||
await sandbox.commands.run(`git -C "${repoPath}" log -1 --pretty=%an`)
|
||||
).stdout.trim()
|
||||
const authorEmail = (
|
||||
await sandbox.commands.run(`git -C "${repoPath}" log -1 --pretty=%ae`)
|
||||
).stdout.trim()
|
||||
|
||||
expect(authorName).toBe(overrideName)
|
||||
expect(authorEmail).toBe(AUTHOR_EMAIL)
|
||||
} finally {
|
||||
await cleanupBaseDir(sandbox, baseDir)
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,87 @@
|
||||
import { expect } from 'vitest'
|
||||
|
||||
import { sandboxTest } from '../../setup.js'
|
||||
import {
|
||||
AUTHOR_EMAIL,
|
||||
AUTHOR_NAME,
|
||||
cleanupBaseDir,
|
||||
createBaseDir,
|
||||
createRepo,
|
||||
} from './helpers.js'
|
||||
|
||||
sandboxTest('git getConfig reads local config', async ({ sandbox }) => {
|
||||
const baseDir = await createBaseDir(sandbox)
|
||||
|
||||
try {
|
||||
const repoPath = await createRepo(sandbox, baseDir)
|
||||
await sandbox.commands.run(
|
||||
`git -C "${repoPath}" config --local pull.rebase true`
|
||||
)
|
||||
|
||||
const value = await sandbox.git.getConfig('pull.rebase', {
|
||||
scope: 'local',
|
||||
path: repoPath,
|
||||
})
|
||||
const commandValue = (
|
||||
await sandbox.commands.run(
|
||||
`git -C "${repoPath}" config --local --get pull.rebase`
|
||||
)
|
||||
).stdout.trim()
|
||||
expect(value).toBe('true')
|
||||
expect(commandValue).toBe('true')
|
||||
} finally {
|
||||
await cleanupBaseDir(sandbox, baseDir)
|
||||
}
|
||||
})
|
||||
|
||||
sandboxTest('git setConfig updates local config', async ({ sandbox }) => {
|
||||
const baseDir = await createBaseDir(sandbox)
|
||||
|
||||
try {
|
||||
const repoPath = await createRepo(sandbox, baseDir)
|
||||
|
||||
await sandbox.git.setConfig('pull.rebase', 'true', {
|
||||
scope: 'local',
|
||||
path: repoPath,
|
||||
})
|
||||
|
||||
const value = (
|
||||
await sandbox.commands.run(
|
||||
`git -C "${repoPath}" config --local --get pull.rebase`
|
||||
)
|
||||
).stdout.trim()
|
||||
const configuredValue = await sandbox.git.getConfig('pull.rebase', {
|
||||
scope: 'local',
|
||||
path: repoPath,
|
||||
})
|
||||
expect(value).toBe('true')
|
||||
expect(configuredValue).toBe('true')
|
||||
} finally {
|
||||
await cleanupBaseDir(sandbox, baseDir)
|
||||
}
|
||||
})
|
||||
|
||||
sandboxTest(
|
||||
'git configureUser sets global user config',
|
||||
async ({ sandbox }) => {
|
||||
await sandbox.git.configureUser(AUTHOR_NAME, AUTHOR_EMAIL)
|
||||
|
||||
const name = (
|
||||
await sandbox.commands.run('git config --global --get user.name')
|
||||
).stdout.trim()
|
||||
const email = (
|
||||
await sandbox.commands.run('git config --global --get user.email')
|
||||
).stdout.trim()
|
||||
const configuredName = await sandbox.git.getConfig('user.name', {
|
||||
scope: 'global',
|
||||
})
|
||||
const configuredEmail = await sandbox.git.getConfig('user.email', {
|
||||
scope: 'global',
|
||||
})
|
||||
|
||||
expect(name).toBe(AUTHOR_NAME)
|
||||
expect(email).toBe(AUTHOR_EMAIL)
|
||||
expect(configuredName).toBe(AUTHOR_NAME)
|
||||
expect(configuredEmail).toBe(AUTHOR_EMAIL)
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,22 @@
|
||||
import { expect } from 'vitest'
|
||||
|
||||
import { sandboxTest } from '../../setup.js'
|
||||
import { HOST, PASSWORD, PROTOCOL, USERNAME } from './helpers.js'
|
||||
|
||||
sandboxTest('git dangerouslyAuthenticate sets helper', async ({ sandbox }) => {
|
||||
await sandbox.git.dangerouslyAuthenticate({
|
||||
username: USERNAME,
|
||||
password: PASSWORD,
|
||||
host: HOST,
|
||||
protocol: PROTOCOL,
|
||||
})
|
||||
|
||||
const helper = (
|
||||
await sandbox.commands.run('git config --global --get credential.helper')
|
||||
).stdout.trim()
|
||||
const configuredHelper = await sandbox.git.getConfig('credential.helper', {
|
||||
scope: 'global',
|
||||
})
|
||||
expect(helper).toBe('store')
|
||||
expect(configuredHelper).toBe('store')
|
||||
})
|
||||
@@ -0,0 +1,57 @@
|
||||
import { randomUUID } from 'node:crypto'
|
||||
|
||||
export const AUTHOR_NAME = 'Sandbox Bot'
|
||||
export const AUTHOR_EMAIL = 'sandbox@example.com'
|
||||
export const USERNAME = 'git'
|
||||
export const PASSWORD = 'token'
|
||||
export const HOST = 'example.com'
|
||||
export const PROTOCOL = 'https'
|
||||
|
||||
const BASE_DIR = '/tmp/test-git'
|
||||
|
||||
export async function createBaseDir(sandbox: any) {
|
||||
const baseDir = `${BASE_DIR}/${randomUUID()}`
|
||||
await sandbox.commands.run(`rm -rf "${baseDir}" && mkdir -p "${baseDir}"`)
|
||||
return baseDir
|
||||
}
|
||||
|
||||
export async function cleanupBaseDir(sandbox: any, baseDir: string) {
|
||||
await sandbox.commands.run(`rm -rf "${baseDir}"`)
|
||||
}
|
||||
|
||||
export async function createRepo(sandbox: any, baseDir: string) {
|
||||
const repoPath = `${baseDir}/repo`
|
||||
await sandbox.git.init(repoPath, { initialBranch: 'main' })
|
||||
return repoPath
|
||||
}
|
||||
|
||||
export async function createRepoWithCommit(sandbox: any, baseDir: string) {
|
||||
const repoPath = await createRepo(sandbox, baseDir)
|
||||
await sandbox.files.write(`${repoPath}/README.md`, 'hello\n')
|
||||
await sandbox.git.add(repoPath)
|
||||
await sandbox.git.commit(repoPath, 'Initial commit', {
|
||||
authorName: AUTHOR_NAME,
|
||||
authorEmail: AUTHOR_EMAIL,
|
||||
})
|
||||
return repoPath
|
||||
}
|
||||
|
||||
export async function startGitDaemon(sandbox: any, baseDir: string) {
|
||||
const remotePath = `${baseDir}/remote.git`
|
||||
await sandbox.commands.run(
|
||||
`git init --bare --initial-branch=main "${remotePath}"`
|
||||
)
|
||||
const port = 9418 + Math.floor(Math.random() * 1000)
|
||||
const handle = await sandbox.commands.run(
|
||||
`git daemon --reuseaddr --base-path="${baseDir}" --export-all ` +
|
||||
`--enable=receive-pack --informative-errors --listen=127.0.0.1 --port=${port}`,
|
||||
{ background: true }
|
||||
)
|
||||
await sandbox.commands.run('sleep 1')
|
||||
return {
|
||||
handle,
|
||||
remotePath,
|
||||
remoteUrl: `git://127.0.0.1:${port}/remote.git`,
|
||||
port,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import { expect } from 'vitest'
|
||||
|
||||
import { sandboxTest } from '../../setup.js'
|
||||
import { cleanupBaseDir, createBaseDir } from './helpers.js'
|
||||
|
||||
sandboxTest('git init', async ({ sandbox }) => {
|
||||
const baseDir = await createBaseDir(sandbox)
|
||||
|
||||
try {
|
||||
const repoPath = `${baseDir}/repo`
|
||||
|
||||
await sandbox.git.init(repoPath, { initialBranch: 'main' })
|
||||
|
||||
expect(await sandbox.files.exists(`${repoPath}/.git`)).toBe(true)
|
||||
const head = (
|
||||
await sandbox.commands.run(
|
||||
`git -C "${repoPath}" symbolic-ref --short HEAD`
|
||||
)
|
||||
).stdout.trim()
|
||||
expect(head).toBe('main')
|
||||
} finally {
|
||||
await cleanupBaseDir(sandbox, baseDir)
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,82 @@
|
||||
import { expect } from 'vitest'
|
||||
|
||||
import { sandboxTest } from '../../setup.js'
|
||||
import {
|
||||
cleanupBaseDir,
|
||||
createBaseDir,
|
||||
createRepo,
|
||||
startGitDaemon,
|
||||
} from './helpers.js'
|
||||
|
||||
sandboxTest(
|
||||
'git remoteGet returns undefined for missing remote',
|
||||
async ({ sandbox }) => {
|
||||
const baseDir = await createBaseDir(sandbox)
|
||||
|
||||
try {
|
||||
const repoPath = await createRepo(sandbox, baseDir)
|
||||
const missingUrl = await sandbox.git.remoteGet(repoPath, 'origin')
|
||||
expect(missingUrl).toBeUndefined()
|
||||
} finally {
|
||||
await cleanupBaseDir(sandbox, baseDir)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
sandboxTest('git remoteAdd adds remote', async ({ sandbox }) => {
|
||||
const baseDir = await createBaseDir(sandbox)
|
||||
|
||||
try {
|
||||
const repoPath = await createRepo(sandbox, baseDir)
|
||||
const daemon = await startGitDaemon(sandbox, baseDir)
|
||||
|
||||
try {
|
||||
await sandbox.git.remoteAdd(repoPath, 'origin', daemon.remoteUrl)
|
||||
const remoteUrl = await sandbox.git.remoteGet(repoPath, 'origin')
|
||||
expect(remoteUrl).toBe(daemon.remoteUrl)
|
||||
} finally {
|
||||
await daemon.handle.kill()
|
||||
}
|
||||
} finally {
|
||||
await cleanupBaseDir(sandbox, baseDir)
|
||||
}
|
||||
})
|
||||
|
||||
sandboxTest('git remoteAdd overwrites existing remote', async ({ sandbox }) => {
|
||||
const baseDir = await createBaseDir(sandbox)
|
||||
|
||||
try {
|
||||
const repoPath = await createRepo(sandbox, baseDir)
|
||||
const daemon = await startGitDaemon(sandbox, baseDir)
|
||||
|
||||
try {
|
||||
await sandbox.git.remoteAdd(repoPath, 'origin', daemon.remoteUrl)
|
||||
const currentUrl = (
|
||||
await sandbox.commands.run(`git -C "${repoPath}" remote get-url origin`)
|
||||
).stdout.trim()
|
||||
const currentRemote = await sandbox.git.remoteGet(repoPath, 'origin')
|
||||
expect(currentUrl).toBe(daemon.remoteUrl)
|
||||
expect(currentRemote).toBe(daemon.remoteUrl)
|
||||
|
||||
const secondPath = `${baseDir}/remote-2.git`
|
||||
await sandbox.commands.run(
|
||||
`git init --bare --initial-branch=main "${secondPath}"`
|
||||
)
|
||||
const secondUrl = `git://127.0.0.1:${daemon.port}/remote-2.git`
|
||||
|
||||
await sandbox.git.remoteAdd(repoPath, 'origin', secondUrl, {
|
||||
overwrite: true,
|
||||
})
|
||||
const updatedUrl = (
|
||||
await sandbox.commands.run(`git -C "${repoPath}" remote get-url origin`)
|
||||
).stdout.trim()
|
||||
const updatedRemote = await sandbox.git.remoteGet(repoPath, 'origin')
|
||||
expect(updatedUrl).toBe(secondUrl)
|
||||
expect(updatedRemote).toBe(secondUrl)
|
||||
} finally {
|
||||
await daemon.handle.kill()
|
||||
}
|
||||
} finally {
|
||||
await cleanupBaseDir(sandbox, baseDir)
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,30 @@
|
||||
import { expect } from 'vitest'
|
||||
|
||||
import { sandboxTest } from '../../setup.js'
|
||||
import {
|
||||
cleanupBaseDir,
|
||||
createBaseDir,
|
||||
createRepoWithCommit,
|
||||
} from './helpers.js'
|
||||
|
||||
sandboxTest('git reset --hard discards changes', async ({ sandbox }) => {
|
||||
const baseDir = await createBaseDir(sandbox)
|
||||
|
||||
try {
|
||||
const repoPath = await createRepoWithCommit(sandbox, baseDir)
|
||||
await sandbox.files.write(`${repoPath}/README.md`, 'changed\n')
|
||||
|
||||
const status = await sandbox.git.status(repoPath)
|
||||
expect(status.isClean).toBe(false)
|
||||
|
||||
await sandbox.git.reset(repoPath, { mode: 'hard', target: 'HEAD' })
|
||||
|
||||
const statusAfter = await sandbox.git.status(repoPath)
|
||||
expect(statusAfter.isClean).toBe(true)
|
||||
|
||||
const contents = await sandbox.files.read(`${repoPath}/README.md`)
|
||||
expect(contents).toBe('hello\n')
|
||||
} finally {
|
||||
await cleanupBaseDir(sandbox, baseDir)
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,58 @@
|
||||
import { expect } from 'vitest'
|
||||
|
||||
import { sandboxTest } from '../../setup.js'
|
||||
import {
|
||||
cleanupBaseDir,
|
||||
createBaseDir,
|
||||
createRepoWithCommit,
|
||||
} from './helpers.js'
|
||||
|
||||
sandboxTest('git restore --staged unstages changes', async ({ sandbox }) => {
|
||||
const baseDir = await createBaseDir(sandbox)
|
||||
|
||||
try {
|
||||
const repoPath = await createRepoWithCommit(sandbox, baseDir)
|
||||
await sandbox.files.write(`${repoPath}/README.md`, 'changed\n')
|
||||
await sandbox.git.add(repoPath, { files: ['README.md'] })
|
||||
|
||||
const status = await sandbox.git.status(repoPath)
|
||||
expect(status.hasStaged).toBe(true)
|
||||
|
||||
await sandbox.git.restore(repoPath, {
|
||||
paths: ['README.md'],
|
||||
staged: true,
|
||||
worktree: false,
|
||||
})
|
||||
|
||||
const statusAfter = await sandbox.git.status(repoPath)
|
||||
expect(statusAfter.hasStaged).toBe(false)
|
||||
expect(statusAfter.hasChanges).toBe(true)
|
||||
} finally {
|
||||
await cleanupBaseDir(sandbox, baseDir)
|
||||
}
|
||||
})
|
||||
|
||||
sandboxTest(
|
||||
'git restore discards working tree changes',
|
||||
async ({ sandbox }) => {
|
||||
const baseDir = await createBaseDir(sandbox)
|
||||
|
||||
try {
|
||||
const repoPath = await createRepoWithCommit(sandbox, baseDir)
|
||||
await sandbox.files.write(`${repoPath}/README.md`, 'changed\n')
|
||||
|
||||
const status = await sandbox.git.status(repoPath)
|
||||
expect(status.isClean).toBe(false)
|
||||
|
||||
await sandbox.git.restore(repoPath, { paths: ['README.md'] })
|
||||
|
||||
const statusAfter = await sandbox.git.status(repoPath)
|
||||
expect(statusAfter.isClean).toBe(true)
|
||||
|
||||
const contents = await sandbox.files.read(`${repoPath}/README.md`)
|
||||
expect(contents).toBe('hello\n')
|
||||
} finally {
|
||||
await cleanupBaseDir(sandbox, baseDir)
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,99 @@
|
||||
import { expect } from 'vitest'
|
||||
|
||||
import { sandboxTest } from '../../setup.js'
|
||||
import {
|
||||
AUTHOR_EMAIL,
|
||||
AUTHOR_NAME,
|
||||
cleanupBaseDir,
|
||||
createBaseDir,
|
||||
createRepo,
|
||||
} from './helpers.js'
|
||||
|
||||
sandboxTest('git status reports untracked file', async ({ sandbox }) => {
|
||||
const baseDir = await createBaseDir(sandbox)
|
||||
|
||||
try {
|
||||
const repoPath = await createRepo(sandbox, baseDir)
|
||||
await sandbox.files.write(`${repoPath}/README.md`, 'hello\n')
|
||||
|
||||
const status = await sandbox.git.status(repoPath)
|
||||
const entry = status.fileStatus.find(
|
||||
(file: any) => file.name === 'README.md'
|
||||
)
|
||||
expect(entry?.status).toBe('untracked')
|
||||
expect(status.isClean).toBe(false)
|
||||
expect(status.hasChanges).toBe(true)
|
||||
expect(status.hasUntracked).toBe(true)
|
||||
expect(status.hasStaged).toBe(false)
|
||||
expect(status.hasConflicts).toBe(false)
|
||||
expect(status.totalCount).toBe(1)
|
||||
expect(status.stagedCount).toBe(0)
|
||||
expect(status.unstagedCount).toBe(1)
|
||||
expect(status.untrackedCount).toBe(1)
|
||||
expect(status.conflictCount).toBe(0)
|
||||
} finally {
|
||||
await cleanupBaseDir(sandbox, baseDir)
|
||||
}
|
||||
})
|
||||
|
||||
sandboxTest(
|
||||
'git status reports added modified deleted renamed',
|
||||
async ({ sandbox }) => {
|
||||
const baseDir = await createBaseDir(sandbox)
|
||||
|
||||
try {
|
||||
const repoPath = await createRepo(sandbox, baseDir)
|
||||
await sandbox.files.write(`${repoPath}/README.md`, 'hello\n')
|
||||
await sandbox.files.write(`${repoPath}/DELETE.md`, 'delete me\n')
|
||||
await sandbox.files.write(`${repoPath}/RENAME.md`, 'rename me\n')
|
||||
await sandbox.git.add(repoPath)
|
||||
await sandbox.git.commit(repoPath, 'Initial commit', {
|
||||
authorName: AUTHOR_NAME,
|
||||
authorEmail: AUTHOR_EMAIL,
|
||||
})
|
||||
|
||||
await sandbox.files.write(`${repoPath}/README.md`, 'hello again\n')
|
||||
await sandbox.files.write(`${repoPath}/NEW.md`, 'new file\n')
|
||||
await sandbox.git.add(repoPath, { files: ['NEW.md'] })
|
||||
await sandbox.commands.run(`git -C "${repoPath}" rm DELETE.md`)
|
||||
await sandbox.commands.run(`git -C "${repoPath}" mv RENAME.md RENAMED.md`)
|
||||
|
||||
const status = await sandbox.git.status(repoPath)
|
||||
|
||||
const modified = status.fileStatus.find(
|
||||
(file: any) => file.name === 'README.md'
|
||||
)
|
||||
const added = status.fileStatus.find(
|
||||
(file: any) => file.name === 'NEW.md'
|
||||
)
|
||||
const deleted = status.fileStatus.find(
|
||||
(file: any) => file.name === 'DELETE.md'
|
||||
)
|
||||
const renamed = status.fileStatus.find(
|
||||
(file: any) => file.name === 'RENAMED.md'
|
||||
)
|
||||
|
||||
expect(modified?.status).toBe('modified')
|
||||
expect(modified?.staged).toBe(false)
|
||||
expect(added?.status).toBe('added')
|
||||
expect(added?.staged).toBe(true)
|
||||
expect(deleted?.status).toBe('deleted')
|
||||
expect(deleted?.staged).toBe(true)
|
||||
expect(renamed?.status).toBe('renamed')
|
||||
expect(renamed?.staged).toBe(true)
|
||||
expect(renamed?.renamedFrom).toBe('RENAME.md')
|
||||
|
||||
expect(status.hasChanges).toBe(true)
|
||||
expect(status.hasStaged).toBe(true)
|
||||
expect(status.hasUntracked).toBe(false)
|
||||
expect(status.hasConflicts).toBe(false)
|
||||
expect(status.totalCount).toBe(4)
|
||||
expect(status.stagedCount).toBe(3)
|
||||
expect(status.unstagedCount).toBe(1)
|
||||
expect(status.untrackedCount).toBe(0)
|
||||
expect(status.conflictCount).toBe(0)
|
||||
} finally {
|
||||
await cleanupBaseDir(sandbox, baseDir)
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,116 @@
|
||||
import { expect } from 'vitest'
|
||||
|
||||
import { sandboxTest } from '../../setup.js'
|
||||
import {
|
||||
AUTHOR_EMAIL,
|
||||
AUTHOR_NAME,
|
||||
cleanupBaseDir,
|
||||
createBaseDir,
|
||||
createRepoWithCommit,
|
||||
startGitDaemon,
|
||||
} from './helpers.js'
|
||||
|
||||
sandboxTest('git push updates remote', async ({ sandbox }) => {
|
||||
const baseDir = await createBaseDir(sandbox)
|
||||
|
||||
try {
|
||||
const repoPath = await createRepoWithCommit(sandbox, baseDir)
|
||||
const daemon = await startGitDaemon(sandbox, baseDir)
|
||||
|
||||
try {
|
||||
await sandbox.git.remoteAdd(repoPath, 'origin', daemon.remoteUrl)
|
||||
await sandbox.git.push(repoPath, {
|
||||
remote: 'origin',
|
||||
branch: 'main',
|
||||
})
|
||||
|
||||
const message = (
|
||||
await sandbox.commands.run(
|
||||
`git --git-dir="${daemon.remotePath}" log -1 --pretty=%B`
|
||||
)
|
||||
).stdout.trim()
|
||||
expect(message).toBe('Initial commit')
|
||||
} finally {
|
||||
await daemon.handle.kill()
|
||||
}
|
||||
} finally {
|
||||
await cleanupBaseDir(sandbox, baseDir)
|
||||
}
|
||||
})
|
||||
|
||||
sandboxTest('git push warns when no upstream', async ({ sandbox }) => {
|
||||
const baseDir = await createBaseDir(sandbox)
|
||||
|
||||
try {
|
||||
const repoPath = await createRepoWithCommit(sandbox, baseDir)
|
||||
const daemon = await startGitDaemon(sandbox, baseDir)
|
||||
|
||||
try {
|
||||
await sandbox.git.remoteAdd(repoPath, 'origin', daemon.remoteUrl)
|
||||
|
||||
await expect(
|
||||
sandbox.git.push(repoPath, { setUpstream: false })
|
||||
).rejects.toThrow(/no upstream branch is configured/i)
|
||||
} finally {
|
||||
await daemon.handle.kill()
|
||||
}
|
||||
} finally {
|
||||
await cleanupBaseDir(sandbox, baseDir)
|
||||
}
|
||||
})
|
||||
|
||||
sandboxTest('git pull updates clone', async ({ sandbox }) => {
|
||||
const baseDir = await createBaseDir(sandbox)
|
||||
|
||||
try {
|
||||
const repoPath = await createRepoWithCommit(sandbox, baseDir)
|
||||
const daemon = await startGitDaemon(sandbox, baseDir)
|
||||
const clonePath = `${baseDir}/clone`
|
||||
|
||||
try {
|
||||
await sandbox.git.remoteAdd(repoPath, 'origin', daemon.remoteUrl)
|
||||
await sandbox.git.push(repoPath, {
|
||||
remote: 'origin',
|
||||
branch: 'main',
|
||||
})
|
||||
await sandbox.git.clone(daemon.remoteUrl, { path: clonePath })
|
||||
|
||||
await sandbox.files.write(`${repoPath}/README.md`, 'hello\nmore\n')
|
||||
await sandbox.git.add(repoPath)
|
||||
await sandbox.git.commit(repoPath, 'Update README', {
|
||||
authorName: AUTHOR_NAME,
|
||||
authorEmail: AUTHOR_EMAIL,
|
||||
})
|
||||
await sandbox.git.push(repoPath)
|
||||
|
||||
await sandbox.git.pull(clonePath)
|
||||
const contents = await sandbox.files.read(`${clonePath}/README.md`)
|
||||
expect(contents).toContain('more')
|
||||
} finally {
|
||||
await daemon.handle.kill()
|
||||
}
|
||||
} finally {
|
||||
await cleanupBaseDir(sandbox, baseDir)
|
||||
}
|
||||
})
|
||||
|
||||
sandboxTest('git pull warns when no upstream', async ({ sandbox }) => {
|
||||
const baseDir = await createBaseDir(sandbox)
|
||||
|
||||
try {
|
||||
const repoPath = await createRepoWithCommit(sandbox, baseDir)
|
||||
const daemon = await startGitDaemon(sandbox, baseDir)
|
||||
|
||||
try {
|
||||
await sandbox.git.remoteAdd(repoPath, 'origin', daemon.remoteUrl)
|
||||
|
||||
await expect(sandbox.git.pull(repoPath)).rejects.toThrow(
|
||||
/no upstream branch is configured/i
|
||||
)
|
||||
} finally {
|
||||
await daemon.handle.kill()
|
||||
}
|
||||
} finally {
|
||||
await cleanupBaseDir(sandbox, baseDir)
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,44 @@
|
||||
import { test, expect } from 'vitest'
|
||||
|
||||
import { Git } from '../../../src/sandbox/git'
|
||||
import type { Commands } from '../../../src/sandbox/commands'
|
||||
import { InvalidArgumentError } from '../../../src/errors'
|
||||
|
||||
// Stub command runner that fails if a git command is actually executed —
|
||||
// validation must throw before reaching it.
|
||||
const failingCommands = {
|
||||
run: () => {
|
||||
throw new Error('commands.run should not be called')
|
||||
},
|
||||
} as unknown as Commands
|
||||
|
||||
test('git.reset throws InvalidArgumentError on an invalid mode', async () => {
|
||||
const git = new Git(failingCommands)
|
||||
await expect(
|
||||
// @ts-expect-error - testing runtime validation with an invalid mode
|
||||
git.reset('/repo', { mode: 'bogus' })
|
||||
).rejects.toThrow(InvalidArgumentError)
|
||||
})
|
||||
|
||||
test('git.reset accepts a valid mode', async () => {
|
||||
const git = new Git(failingCommands)
|
||||
// A valid mode must pass validation and reach the (stubbed) command runner.
|
||||
await expect(git.reset('/repo', { mode: 'hard' })).rejects.toThrow(
|
||||
'commands.run should not be called'
|
||||
)
|
||||
})
|
||||
|
||||
test('git.remoteAdd throws InvalidArgumentError when name or url is missing', async () => {
|
||||
const git = new Git(failingCommands)
|
||||
await expect(
|
||||
git.remoteAdd('/repo', '', 'https://example.com')
|
||||
).rejects.toThrow(InvalidArgumentError)
|
||||
await expect(git.remoteAdd('/repo', 'origin', '')).rejects.toThrow(
|
||||
InvalidArgumentError
|
||||
)
|
||||
})
|
||||
|
||||
test('git.remoteGet throws InvalidArgumentError when name is missing', async () => {
|
||||
const git = new Git(failingCommands)
|
||||
await expect(git.remoteGet('/repo', '')).rejects.toThrow(InvalidArgumentError)
|
||||
})
|
||||
Reference in New Issue
Block a user