status_test.go 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. package git
  2. import (
  3. "os"
  4. "path/filepath"
  5. "testing"
  6. gogit "github.com/go-git/go-git/v5"
  7. )
  8. func TestStatusOnEmptyRepository(t *testing.T) {
  9. manager := newTestManager(t)
  10. repoPath := newTestRepo(t, manager)
  11. status, err := manager.Status(repoPath)
  12. if err != nil {
  13. t.Fatalf("Status() returned error: %v", err)
  14. }
  15. if !status.Clean {
  16. t.Errorf("Clean = false on a fresh repository, want true")
  17. }
  18. if status.Head != nil {
  19. t.Errorf("Head = %+v on an unborn branch, want nil", status.Head)
  20. }
  21. if status.Branch == "" {
  22. t.Errorf("Branch = \"\" on an unborn branch, want the default branch name")
  23. }
  24. if len(status.Changes) != 0 {
  25. t.Errorf("Changes = %d entries, want 0", len(status.Changes))
  26. }
  27. }
  28. func TestStatusReportsUntrackedAndStagedFiles(t *testing.T) {
  29. manager := newTestManager(t)
  30. repoPath := newTestRepo(t, manager)
  31. writeFile(t, repoPath, "untracked.txt", "hello\n")
  32. writeFile(t, repoPath, "staged.txt", "world\n")
  33. if err := manager.Add(repoPath, []string{"staged.txt"}); err != nil {
  34. t.Fatalf("Add() returned error: %v", err)
  35. }
  36. status, err := manager.Status(repoPath)
  37. if err != nil {
  38. t.Fatalf("Status() returned error: %v", err)
  39. }
  40. if status.Clean {
  41. t.Errorf("Clean = true with pending changes, want false")
  42. }
  43. if len(status.Changes) != 2 {
  44. t.Fatalf("Changes = %d entries, want 2 (%+v)", len(status.Changes), status.Changes)
  45. }
  46. byPath := map[string]FileChange{}
  47. for _, change := range status.Changes {
  48. byPath[change.Path] = change
  49. }
  50. if got := byPath["untracked.txt"]; got.Status != "untracked" || got.Staged {
  51. t.Errorf("untracked.txt = %+v, want status untracked and Staged false", got)
  52. }
  53. if got := byPath["staged.txt"]; !got.Staged || got.Status != "added" {
  54. t.Errorf("staged.txt = %+v, want status added and Staged true", got)
  55. }
  56. if got := byPath["staged.txt"]; got.Size != int64(len("world\n")) {
  57. t.Errorf("staged.txt Size = %d, want %d", got.Size, len("world\n"))
  58. }
  59. }
  60. func TestStatusAfterCommitIsClean(t *testing.T) {
  61. manager := newTestManager(t)
  62. repoPath := newTestRepo(t, manager)
  63. hash := commitFile(t, manager, repoPath, "readme.md", "# demo\n", "initial commit")
  64. status, err := manager.Status(repoPath)
  65. if err != nil {
  66. t.Fatalf("Status() returned error: %v", err)
  67. }
  68. if !status.Clean {
  69. t.Errorf("Clean = false right after a commit, want true (%+v)", status.Changes)
  70. }
  71. if status.Head == nil {
  72. t.Fatalf("Head = nil after a commit, want the new commit")
  73. }
  74. if status.Head.Hash != hash {
  75. t.Errorf("Head.Hash = %q, want %q", status.Head.Hash, hash)
  76. }
  77. if status.Head.Subject != "initial commit" {
  78. t.Errorf("Head.Subject = %q, want %q", status.Head.Subject, "initial commit")
  79. }
  80. if status.Ahead != 1 {
  81. t.Errorf("Ahead = %d for a never-pushed branch with one commit, want 1", status.Ahead)
  82. }
  83. }
  84. func TestStatusReportsModifiedAndDeleted(t *testing.T) {
  85. manager := newTestManager(t)
  86. repoPath := newTestRepo(t, manager)
  87. commitFile(t, manager, repoPath, "keep.txt", "one\n", "add keep")
  88. commitFile(t, manager, repoPath, "gone.txt", "two\n", "add gone")
  89. writeFile(t, repoPath, "keep.txt", "one changed\n")
  90. if err := os.Remove(filepath.Join(repoPath, "gone.txt")); err != nil {
  91. t.Fatalf("cannot delete file: %v", err)
  92. }
  93. status, err := manager.Status(repoPath)
  94. if err != nil {
  95. t.Fatalf("Status() returned error: %v", err)
  96. }
  97. byPath := map[string]FileChange{}
  98. for _, change := range status.Changes {
  99. byPath[change.Path] = change
  100. }
  101. if got := byPath["keep.txt"]; got.Status != "modified" {
  102. t.Errorf("keep.txt Status = %q, want %q", got.Status, "modified")
  103. }
  104. if got := byPath["gone.txt"]; got.Status != "deleted" {
  105. t.Errorf("gone.txt Status = %q, want %q", got.Status, "deleted")
  106. }
  107. if got := byPath["gone.txt"]; got.Size != -1 {
  108. t.Errorf("gone.txt Size = %d, want -1 for a file that is gone", got.Size)
  109. }
  110. }
  111. func TestStatusFlagsBinaryFiles(t *testing.T) {
  112. manager := newTestManager(t)
  113. repoPath := newTestRepo(t, manager)
  114. if err := os.WriteFile(filepath.Join(repoPath, "blob.bin"), []byte{0x01, 0x00, 0x02}, 0664); err != nil {
  115. t.Fatalf("cannot write binary file: %v", err)
  116. }
  117. status, err := manager.Status(repoPath)
  118. if err != nil {
  119. t.Fatalf("Status() returned error: %v", err)
  120. }
  121. if len(status.Changes) != 1 {
  122. t.Fatalf("Changes = %d entries, want 1", len(status.Changes))
  123. }
  124. if !status.Changes[0].Binary {
  125. t.Errorf("Binary = false for a file containing a NUL byte, want true")
  126. }
  127. }
  128. func TestStatusCodeToString(t *testing.T) {
  129. tests := []struct {
  130. name string
  131. code gogit.StatusCode
  132. want string
  133. }{
  134. {name: "unmodified", code: gogit.Unmodified, want: "unmodified"},
  135. {name: "untracked", code: gogit.Untracked, want: "untracked"},
  136. {name: "modified", code: gogit.Modified, want: "modified"},
  137. {name: "added", code: gogit.Added, want: "added"},
  138. {name: "deleted", code: gogit.Deleted, want: "deleted"},
  139. {name: "renamed", code: gogit.Renamed, want: "renamed"},
  140. {name: "copied", code: gogit.Copied, want: "copied"},
  141. {name: "unmerged", code: gogit.UpdatedButUnmerged, want: "conflicted"},
  142. {name: "unknown code", code: gogit.StatusCode('Z'), want: "unknown"},
  143. }
  144. for _, test := range tests {
  145. t.Run(test.name, func(t *testing.T) {
  146. if got := statusCodeToString(test.code); got != test.want {
  147. t.Errorf("statusCodeToString(%q) = %q, want %q", string(test.code), got, test.want)
  148. }
  149. })
  150. }
  151. }
  152. func TestFileLooksBinary(t *testing.T) {
  153. folder := t.TempDir()
  154. tests := []struct {
  155. name string
  156. content []byte
  157. want bool
  158. }{
  159. {name: "plain text", content: []byte("hello world\n"), want: false},
  160. {name: "empty file", content: []byte{}, want: false},
  161. {name: "contains NUL", content: []byte("abc\x00def"), want: true},
  162. {name: "utf8 text", content: []byte("café 中文\n"), want: false},
  163. }
  164. for _, test := range tests {
  165. t.Run(test.name, func(t *testing.T) {
  166. path := filepath.Join(folder, test.name)
  167. if err := os.WriteFile(path, test.content, 0664); err != nil {
  168. t.Fatalf("cannot write test file: %v", err)
  169. }
  170. if got := fileLooksBinary(path); got != test.want {
  171. t.Errorf("fileLooksBinary(%s) = %v, want %v", test.name, got, test.want)
  172. }
  173. })
  174. }
  175. }