package solutions.tretter.githugandroid import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Assume.assumeTrue import org.junit.Test import java.io.File import java.nio.file.Files class GitSandboxEngineTest { @Test fun statusShowsDeletedTrackedFiles() { val repo = RepoState( initialized = true, files = listOf(GitFile("deleteme.rb", tracked = true, deleted = true)), commits = listOf(CommitNode("0000001", "Added a temp file")), branches = mapOf("master" to 1), ) val (_, output) = GitSandboxEngine.execute(repo, "git status") assertTrue(output.any { it.contains("deleted:") && it.contains("deleteme.rb") }) } @Test fun gitRmRemovesDeletedTrackedFileFromIndex() { val repo = RepoState( initialized = true, files = listOf(GitFile("deleteme.rb", tracked = true, deleted = true)), commits = listOf(CommitNode("0000001", "Added a temp file")), branches = mapOf("master" to 1), ) val (updatedRepo, _) = GitSandboxEngine.execute(repo, "git rm deleteme.rb") assertFalse(updatedRepo.files.any { it.name == "deleteme.rb" }) } @Test fun gitMvExpandsWildcardSourcesIntoDestinationDirectory() { val repo = RepoState( initialized = true, files = listOf( GitFile("about.html", tracked = true), GitFile("contact.html", tracked = true), GitFile("index.html", tracked = true), GitFile("README", tracked = true), ), branches = mapOf("master" to 1), ) val (updatedRepo, _) = GitSandboxEngine.execute(repo, "git mv *.html src") assertTrue(updatedRepo.files.any { it.name == "src/about.html" }) assertTrue(updatedRepo.files.any { it.name == "src/contact.html" }) assertTrue(updatedRepo.files.any { it.name == "src/index.html" }) assertTrue(updatedRepo.files.any { it.name == "README" }) } @Test fun singleQuotedWildcardIsNotExpanded() { val repo = RepoState( initialized = true, files = listOf(GitFile("README"), GitFile("main.kt")), ) val (_, output) = GitSandboxEngine.execute(repo, "echo '*'") assertEquals(listOf("*"), output) } @Test fun echoRedirectAcceptsNoSurroundingSpaces() { val repo = RepoState(initialized = true) val (updatedRepo, output) = GitSandboxEngine.execute(repo, "echo '*.swp'>.gitignore") assertTrue(output.isEmpty()) assertEquals("*.swp", updatedRepo.files.single { it.name == ".gitignore" }.content) } @Test fun commitHashAnswerAcceptsFullHashThatStartsWithDisplayedShortHash() { val repo = RepoState( initialized = true, commits = listOf(CommitNode("abc1234", "Latest commit")), branches = mapOf("master" to 1), ) assertTrue(commitHashAnswer("0000001")(repo, "abc1234fedcba9876543210fedcba9876543210")) } @Test fun mdIsAcceptedAsMkdirAlias() { val repo = RepoState(initialized = true) val (_, output) = GitSandboxEngine.execute(repo, "md src") assertTrue(output.isEmpty()) } @Test fun dirIsAcceptedAsLsAlias() { val repo = RepoState( initialized = true, files = listOf(GitFile("README"), GitFile("src/main.kt")), ) val (_, output) = GitSandboxEngine.execute(repo, "dir") assertTrue("README" in output) assertTrue("src/main.kt" in output) } @Test fun lsShowsDotfiles() { val repo = RepoState( initialized = true, files = listOf(GitFile(".gitignore"), GitFile("README")), ) val (_, output) = GitSandboxEngine.execute(repo, "ls") assertTrue(".gitignore" in output) } @Test fun lsShowsGitDirectoryEntry() { val repo = RepoState(initialized = true) val (_, output) = GitSandboxEngine.execute(repo, "ls") assertTrue(".git" in output) } @Test fun fileCompletionUsesCurrentDirectory() { val repo = RepoState( initialized = true, currentDir = "src", files = listOf( GitFile("README"), GitFile("src/main.kt"), GitFile("src/model/User.kt"), ), ) assertEquals(listOf("main.kt", "model/User.kt"), fileCompletionCandidates(repo).sorted()) assertEquals(listOf("model/"), directoryCompletionCandidates(repo)) } @Test fun nativeCompletionUsesCurrentDirectoryIncludingGitMetadata() { val git = testGitBinary() assumeTrue(git.exists() && git.canExecute()) val root = Files.createTempDirectory("githug-completion").toFile() try { val runtime = GitRepositoryRuntime(root, git) val level = level( id = "completion-test", title = "Completion Test", description = "", hints = emptyList(), commandSuggestions = emptyList(), setup = { RepoState(initialized = true) }, validator = { _, _ -> false }, ) val repo = runtime.prepareLevel(level) val (gitDirRepo, _) = runtime.execute(level, repo, "cd .git") assertTrue("HEAD" in runtime.completionCandidates(level, gitDirRepo, directoriesOnly = false)) } finally { root.deleteRecursively() } } @Test fun nativePushLevelHasDivergedStatusAndRebasesCleanly() { val git = testGitBinary() assumeTrue(git.exists() && git.canExecute()) val root = Files.createTempDirectory("githug-push-level").toFile() try { val runtime = GitRepositoryRuntime(root, git) val level = pushLevel() val repo = runtime.prepareLevel(level) val (_, statusOutput) = runtime.execute(level, repo, "git status") assertTrue(statusOutput.any { it.contains("diverged", ignoreCase = true) }) val (_, diffOutput) = runtime.execute(level, repo, "git diff") assertTrue(diffOutput.isNotEmpty()) val (rebasedRepo, pullOutput) = runtime.execute(level, repo, "git pull --rebase origin master") assertFalse(pullOutput.any { it.contains("fatal:", ignoreCase = true) || it.contains("error:", ignoreCase = true) }) assertTrue(rebasedRepo.files.any { it.name == "file4" && it.tracked }) } finally { root.deleteRecursively() } } @Test fun cdDotDotShortcutMovesToParentDirectory() { val repo = RepoState(initialized = true, currentDir = "src/main") val (updatedRepo, output) = GitSandboxEngine.execute(repo, "cd..") assertTrue(output.isEmpty()) assertEquals("src", updatedRepo.currentDir) } @Test fun delIsAcceptedAsRmAlias() { val repo = RepoState( initialized = true, files = listOf(GitFile("deleteme.txt")), ) val (updatedRepo, output) = GitSandboxEngine.execute(repo, "del deleteme.txt") assertTrue(output.isEmpty()) assertFalse(updatedRepo.files.any { it.name == "deleteme.txt" }) } private fun testGitBinary(): File { System.getenv("GITHUG_TEST_GIT_BINARY") ?.takeIf { it.isNotBlank() } ?.let { File(it) } ?.takeIf { it.exists() && it.canExecute() } ?.let { return it } val repoHostGit = File(repoRoot(), "build/host-git/libgit.so") if (repoHostGit.exists() && repoHostGit.canExecute()) return repoHostGit return File("/usr/bin/git") } private fun repoRoot(): File { val userDir = File(System.getProperty("user.dir") ?: ".") return if (File(userDir, "app/build.gradle.kts").exists()) userDir else userDir.parentFile ?: userDir } }