vcs_test.go 13 KB

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