vcs_test.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534
  1. package vcs
  2. import (
  3. "context"
  4. "encoding/json"
  5. "errors"
  6. "fmt"
  7. "io"
  8. "os"
  9. "os/exec"
  10. "strings"
  11. "testing"
  12. gosrc "github.com/Morganamilo/go-srcinfo"
  13. "github.com/bradleyjkemp/cupaloy"
  14. "github.com/stretchr/testify/assert"
  15. "github.com/stretchr/testify/require"
  16. "github.com/Jguer/yay/v11/pkg/db"
  17. "github.com/Jguer/yay/v11/pkg/settings/exe"
  18. "github.com/Jguer/yay/v11/pkg/text"
  19. )
  20. func TestParsing(t *testing.T) {
  21. t.Parallel()
  22. type source struct {
  23. URL string
  24. Branch string
  25. Protocols []string
  26. }
  27. urls := []string{
  28. "git+https://github.com/neovim/neovim.git",
  29. "git://github.com/jguer/yay.git#branch=master",
  30. "git://github.com/davidgiven/ack",
  31. "git://github.com/jguer/yay.git#tag=v3.440",
  32. "git://github.com/jguer/yay.git#commit=e5470c88c6e2f9e0f97deb4728659ffa70ef5d0c",
  33. "a+b+c+d+e+f://github.com/jguer/yay.git#branch=foo",
  34. }
  35. sources := []source{
  36. {"github.com/neovim/neovim.git", "HEAD", []string{"https"}},
  37. {"github.com/jguer/yay.git", "master", []string{"git"}},
  38. {"github.com/davidgiven/ack", "HEAD", []string{"git"}},
  39. {"", "", nil},
  40. {"", "", nil},
  41. {"", "", nil},
  42. }
  43. for n, url := range urls {
  44. url, branch, protocols := parseSource(url)
  45. compare := sources[n]
  46. assert.Equal(t, compare.URL, url)
  47. assert.Equal(t, compare.Branch, branch)
  48. assert.Equal(t, compare.Protocols, protocols)
  49. }
  50. }
  51. func TestNewInfoStore(t *testing.T) {
  52. t.Parallel()
  53. type args struct {
  54. filePath string
  55. cmdBuilder *exe.CmdBuilder
  56. }
  57. tests := []struct {
  58. name string
  59. args args
  60. }{
  61. {
  62. name: "normal",
  63. args: args{
  64. "/tmp/a.json",
  65. &exe.CmdBuilder{GitBin: "git", GitFlags: []string{"--a", "--b"}, Runner: &exe.OSRunner{}},
  66. },
  67. },
  68. }
  69. for _, tt := range tests {
  70. tt := tt
  71. t.Run(tt.name, func(t *testing.T) {
  72. t.Parallel()
  73. got := NewInfoStore(tt.args.filePath, tt.args.cmdBuilder,
  74. text.NewLogger(io.Discard, strings.NewReader(""), true, "test"))
  75. assert.NotNil(t, got)
  76. assert.Equal(t, []string{"--a", "--b"}, got.CmdBuilder.(*exe.CmdBuilder).GitFlags)
  77. assert.Equal(t, tt.args.cmdBuilder, got.CmdBuilder)
  78. assert.Equal(t, "/tmp/a.json", got.FilePath)
  79. })
  80. }
  81. }
  82. type MockRunner struct {
  83. Returned []string
  84. Index int
  85. }
  86. func (r *MockRunner) Show(cmd *exec.Cmd) error {
  87. return nil
  88. }
  89. func (r *MockRunner) Capture(cmd *exec.Cmd) (stdout, stderr string, err error) {
  90. stdout = r.Returned[r.Index]
  91. if r.Returned[0] == "error" {
  92. err = errors.New("possible error")
  93. }
  94. return stdout, stderr, err
  95. }
  96. func TestInfoStoreToUpgrade(t *testing.T) {
  97. t.Parallel()
  98. type fields struct {
  99. CmdBuilder *exe.CmdBuilder
  100. }
  101. type args struct {
  102. infos OriginInfoByURL
  103. }
  104. tests := []struct {
  105. name string
  106. fields fields
  107. args args
  108. want bool
  109. }{
  110. {
  111. name: "simple-has_update",
  112. args: args{infos: OriginInfoByURL{
  113. "github.com/Jguer/z.git": OriginInfo{
  114. Protocols: []string{"https"},
  115. Branch: "0",
  116. SHA: "991c5b4146fd27f4aacf4e3111258a848934aaa1",
  117. },
  118. }}, fields: fields{
  119. CmdBuilder: &exe.CmdBuilder{GitBin: "git", GitFlags: []string{""}, Runner: &MockRunner{
  120. Returned: []string{"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa HEAD"},
  121. }},
  122. },
  123. want: true,
  124. },
  125. {
  126. name: "double-has_update",
  127. args: args{infos: OriginInfoByURL{
  128. "github.com/Jguer/z.git": OriginInfo{
  129. Protocols: []string{"https"},
  130. Branch: "0",
  131. SHA: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
  132. },
  133. "github.com/Jguer/a.git": OriginInfo{
  134. Protocols: []string{"https"},
  135. Branch: "0",
  136. SHA: "991c5b4146fd27f4aacf4e3111258a848934aaa1",
  137. },
  138. }}, fields: fields{
  139. CmdBuilder: &exe.CmdBuilder{GitBin: "git", GitFlags: []string{""}, Runner: &MockRunner{
  140. Returned: []string{
  141. "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa HEAD",
  142. "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa HEAD",
  143. },
  144. }},
  145. },
  146. want: true,
  147. },
  148. {
  149. name: "simple-no_update",
  150. args: args{infos: OriginInfoByURL{
  151. "github.com/Jguer/z.git": OriginInfo{
  152. Protocols: []string{"https"},
  153. Branch: "0",
  154. SHA: "991c5b4146fd27f4aacf4e3111258a848934aaa1",
  155. },
  156. }}, fields: fields{
  157. CmdBuilder: &exe.CmdBuilder{GitBin: "git", GitFlags: []string{""}, Runner: &MockRunner{
  158. Returned: []string{"991c5b4146fd27f4aacf4e3111258a848934aaa1 HEAD"},
  159. }},
  160. },
  161. want: false,
  162. },
  163. {
  164. name: "simple-no_split",
  165. args: args{infos: OriginInfoByURL{
  166. "github.com/Jguer/z.git": OriginInfo{
  167. Protocols: []string{"https"},
  168. Branch: "0",
  169. SHA: "991c5b4146fd27f4aacf4e3111258a848934aaa1",
  170. },
  171. }}, fields: fields{
  172. CmdBuilder: &exe.CmdBuilder{GitBin: "git", GitFlags: []string{""}, Runner: &MockRunner{
  173. Returned: []string{"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"},
  174. }},
  175. },
  176. want: false,
  177. },
  178. {
  179. name: "simple-error",
  180. args: args{infos: OriginInfoByURL{
  181. "github.com/Jguer/z.git": OriginInfo{
  182. Protocols: []string{"https"},
  183. Branch: "0",
  184. SHA: "991c5b4146fd27f4aacf4e3111258a848934aaa1",
  185. },
  186. }}, fields: fields{
  187. CmdBuilder: &exe.CmdBuilder{
  188. GitBin: "git", GitFlags: []string{""},
  189. Runner: &MockRunner{
  190. Returned: []string{"error"},
  191. },
  192. },
  193. },
  194. want: false,
  195. },
  196. {
  197. name: "simple-no protocol",
  198. args: args{infos: OriginInfoByURL{
  199. "github.com/Jguer/z.git": OriginInfo{
  200. Protocols: []string{},
  201. Branch: "0",
  202. SHA: "991c5b4146fd27f4aacf4e3111258a848934aaa1",
  203. },
  204. }}, fields: fields{
  205. CmdBuilder: &exe.CmdBuilder{GitBin: "git", GitFlags: []string{""}, Runner: &MockRunner{
  206. Returned: []string{"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"},
  207. }},
  208. },
  209. want: false,
  210. },
  211. }
  212. for _, tt := range tests {
  213. tt := tt
  214. t.Run(tt.name, func(t *testing.T) {
  215. t.Parallel()
  216. v := &InfoStore{
  217. logger: text.GlobalLogger,
  218. CmdBuilder: tt.fields.CmdBuilder,
  219. OriginsByPackage: map[string]OriginInfoByURL{
  220. "yay": tt.args.infos,
  221. },
  222. }
  223. got := v.ToUpgrade(context.Background(), "yay")
  224. assert.Equal(t, tt.want, got)
  225. })
  226. }
  227. }
  228. func TestInfoStore_NeedsUpdate(t *testing.T) {
  229. t.Parallel()
  230. type fields struct {
  231. CmdBuilder *exe.CmdBuilder
  232. }
  233. type args struct {
  234. infos OriginInfoByURL
  235. }
  236. tests := []struct {
  237. name string
  238. fields fields
  239. args args
  240. want bool
  241. }{
  242. {
  243. name: "simple-has_update",
  244. args: args{infos: OriginInfoByURL{
  245. "github.com/Jguer/z.git": OriginInfo{
  246. Protocols: []string{"https"},
  247. Branch: "0",
  248. SHA: "991c5b4146fd27f4aacf4e3111258a848934aaa1",
  249. },
  250. }}, fields: fields{
  251. CmdBuilder: &exe.CmdBuilder{GitBin: "git", GitFlags: []string{""}, Runner: &MockRunner{
  252. Returned: []string{"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa HEAD"},
  253. }},
  254. },
  255. want: true,
  256. },
  257. {
  258. name: "double-has_update",
  259. args: args{infos: OriginInfoByURL{
  260. "github.com/Jguer/z.git": OriginInfo{
  261. Protocols: []string{"https"},
  262. Branch: "0",
  263. SHA: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
  264. },
  265. "github.com/Jguer/a.git": OriginInfo{
  266. Protocols: []string{"https"},
  267. Branch: "0",
  268. SHA: "991c5b4146fd27f4aacf4e3111258a848934aaa1",
  269. },
  270. }}, fields: fields{
  271. CmdBuilder: &exe.CmdBuilder{GitBin: "git", GitFlags: []string{""}, Runner: &MockRunner{
  272. Returned: []string{
  273. "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa HEAD",
  274. "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa HEAD",
  275. },
  276. }},
  277. },
  278. want: true,
  279. },
  280. {
  281. name: "simple-no_update",
  282. args: args{infos: OriginInfoByURL{
  283. "github.com/Jguer/z.git": OriginInfo{
  284. Protocols: []string{"https"},
  285. Branch: "0",
  286. SHA: "991c5b4146fd27f4aacf4e3111258a848934aaa1",
  287. },
  288. }}, fields: fields{
  289. CmdBuilder: &exe.CmdBuilder{GitBin: "git", GitFlags: []string{""}, Runner: &MockRunner{
  290. Returned: []string{"991c5b4146fd27f4aacf4e3111258a848934aaa1 HEAD"},
  291. }},
  292. },
  293. want: false,
  294. },
  295. {
  296. name: "simple-no_split",
  297. args: args{infos: OriginInfoByURL{
  298. "github.com/Jguer/z.git": OriginInfo{
  299. Protocols: []string{"https"},
  300. Branch: "0",
  301. SHA: "991c5b4146fd27f4aacf4e3111258a848934aaa1",
  302. },
  303. }}, fields: fields{
  304. CmdBuilder: &exe.CmdBuilder{GitBin: "git", GitFlags: []string{""}, Runner: &MockRunner{
  305. Returned: []string{"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"},
  306. }},
  307. },
  308. want: false,
  309. },
  310. {
  311. name: "simple-error",
  312. args: args{infos: OriginInfoByURL{
  313. "github.com/Jguer/z.git": OriginInfo{
  314. Protocols: []string{"https"},
  315. Branch: "0",
  316. SHA: "991c5b4146fd27f4aacf4e3111258a848934aaa1",
  317. },
  318. }}, fields: fields{
  319. CmdBuilder: &exe.CmdBuilder{
  320. GitBin: "git", GitFlags: []string{""},
  321. Runner: &MockRunner{
  322. Returned: []string{"error"},
  323. },
  324. },
  325. },
  326. want: false,
  327. },
  328. {
  329. name: "simple-no protocol",
  330. args: args{infos: OriginInfoByURL{
  331. "github.com/Jguer/z.git": OriginInfo{
  332. Protocols: []string{},
  333. Branch: "0",
  334. SHA: "991c5b4146fd27f4aacf4e3111258a848934aaa1",
  335. },
  336. }}, fields: fields{
  337. CmdBuilder: &exe.CmdBuilder{GitBin: "git", GitFlags: []string{""}, Runner: &MockRunner{
  338. Returned: []string{"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"},
  339. }},
  340. },
  341. want: false,
  342. },
  343. }
  344. for _, tt := range tests {
  345. tt := tt
  346. t.Run(tt.name, func(t *testing.T) {
  347. t.Parallel()
  348. v := &InfoStore{
  349. logger: text.GlobalLogger,
  350. CmdBuilder: tt.fields.CmdBuilder,
  351. }
  352. got := v.needsUpdate(context.Background(), tt.args.infos)
  353. assert.Equal(t, tt.want, got)
  354. })
  355. }
  356. }
  357. func TestInfoStore_Update(t *testing.T) {
  358. t.Parallel()
  359. type fields struct {
  360. OriginsByPackage map[string]OriginInfoByURL
  361. CmdBuilder *exe.CmdBuilder
  362. }
  363. type args struct {
  364. pkgName string
  365. sources []gosrc.ArchString
  366. }
  367. tests := []struct {
  368. name string
  369. fields fields
  370. args args
  371. }{
  372. {
  373. name: "simple",
  374. args: args{
  375. pkgName: "hello",
  376. sources: []gosrc.ArchString{{Value: "git://github.com/jguer/yay.git#branch=master"}},
  377. },
  378. fields: fields{
  379. OriginsByPackage: make(map[string]OriginInfoByURL),
  380. CmdBuilder: &exe.CmdBuilder{
  381. GitBin: "git", GitFlags: []string{""},
  382. Runner: &MockRunner{Returned: []string{"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa HEAD"}},
  383. },
  384. },
  385. },
  386. }
  387. file, err := os.CreateTemp("/tmp", "yay-infostore-*-test")
  388. filePath := file.Name()
  389. require.NoError(t, err)
  390. for _, tt := range tests {
  391. tt := tt
  392. t.Run(tt.name, func(t *testing.T) {
  393. t.Parallel()
  394. v := &InfoStore{
  395. OriginsByPackage: tt.fields.OriginsByPackage,
  396. logger: text.GlobalLogger,
  397. FilePath: filePath,
  398. CmdBuilder: tt.fields.CmdBuilder,
  399. }
  400. v.Update(context.Background(), tt.args.pkgName, tt.args.sources)
  401. assert.Len(t, tt.fields.OriginsByPackage, 1)
  402. marshalledinfo, err := json.MarshalIndent(tt.fields.OriginsByPackage, "", "\t")
  403. assert.NoError(t, err)
  404. cupaloy.SnapshotT(t, marshalledinfo)
  405. v.Load()
  406. fmt.Println(v.OriginsByPackage)
  407. assert.Len(t, tt.fields.OriginsByPackage, 1)
  408. marshalledinfo, err = json.MarshalIndent(tt.fields.OriginsByPackage, "", "\t")
  409. assert.NoError(t, err)
  410. cupaloy.SnapshotT(t, marshalledinfo)
  411. })
  412. }
  413. require.NoError(t, os.Remove(filePath))
  414. }
  415. func TestInfoStore_Remove(t *testing.T) {
  416. t.Parallel()
  417. type fields struct {
  418. OriginsByPackage map[string]OriginInfoByURL
  419. }
  420. type args struct {
  421. pkgs []string
  422. }
  423. tests := []struct {
  424. name string
  425. fields fields
  426. args args
  427. }{
  428. {
  429. name: "simple",
  430. args: args{pkgs: []string{"a", "c"}},
  431. fields: fields{
  432. OriginsByPackage: map[string]OriginInfoByURL{
  433. "a": {},
  434. "b": {},
  435. "c": {},
  436. "d": {},
  437. },
  438. },
  439. },
  440. }
  441. file, err := os.CreateTemp("/tmp", "yay-vcs-*-test")
  442. filePath := file.Name()
  443. require.NoError(t, err)
  444. for _, tt := range tests {
  445. tt := tt
  446. t.Run(tt.name, func(t *testing.T) {
  447. t.Parallel()
  448. v := &InfoStore{
  449. OriginsByPackage: tt.fields.OriginsByPackage,
  450. logger: text.GlobalLogger,
  451. FilePath: filePath,
  452. }
  453. v.RemovePackages(tt.args.pkgs)
  454. assert.Len(t, tt.fields.OriginsByPackage, 2)
  455. })
  456. }
  457. require.NoError(t, os.Remove(filePath))
  458. }
  459. func TestInfoStore_CleanOrphans(t *testing.T) {
  460. t.Parallel()
  461. type fields struct {
  462. OriginsByPackage map[string]OriginInfoByURL
  463. }
  464. type args struct {
  465. pkgs map[string]db.IPackage
  466. }
  467. tests := []struct {
  468. name string
  469. fields fields
  470. args args
  471. }{
  472. {
  473. name: "simple",
  474. args: args{pkgs: map[string]db.IPackage{"a": nil, "b": nil, "d": nil}},
  475. fields: fields{
  476. OriginsByPackage: map[string]OriginInfoByURL{
  477. "a": {},
  478. "b": {},
  479. "c": {},
  480. "d": {},
  481. },
  482. },
  483. },
  484. }
  485. file, err := os.CreateTemp("/tmp", "yay-vcs-*-test")
  486. filePath := file.Name()
  487. require.NoError(t, err)
  488. for _, tt := range tests {
  489. tt := tt
  490. t.Run(tt.name, func(t *testing.T) {
  491. t.Parallel()
  492. v := &InfoStore{
  493. OriginsByPackage: tt.fields.OriginsByPackage,
  494. FilePath: filePath,
  495. logger: text.NewLogger(io.Discard, strings.NewReader(""), false, "test"),
  496. }
  497. v.CleanOrphans(tt.args.pkgs)
  498. assert.Len(t, tt.fields.OriginsByPackage, 3)
  499. })
  500. }
  501. require.NoError(t, os.Remove(filePath))
  502. }