service_test.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597
  1. package upgrade
  2. import (
  3. "context"
  4. "io"
  5. "strings"
  6. "testing"
  7. "github.com/Jguer/aur"
  8. "github.com/Jguer/go-alpm/v2"
  9. "github.com/stretchr/testify/assert"
  10. "github.com/stretchr/testify/require"
  11. "github.com/Jguer/yay/v12/pkg/db"
  12. "github.com/Jguer/yay/v12/pkg/db/mock"
  13. "github.com/Jguer/yay/v12/pkg/dep"
  14. "github.com/Jguer/yay/v12/pkg/query"
  15. "github.com/Jguer/yay/v12/pkg/settings"
  16. "github.com/Jguer/yay/v12/pkg/settings/parser"
  17. "github.com/Jguer/yay/v12/pkg/text"
  18. "github.com/Jguer/yay/v12/pkg/topo"
  19. "github.com/Jguer/yay/v12/pkg/vcs"
  20. mockaur "github.com/Jguer/yay/v12/pkg/dep/mock"
  21. )
  22. func ptrString(s string) *string {
  23. return &s
  24. }
  25. func TestUpgradeService_GraphUpgrades(t *testing.T) {
  26. t.Parallel()
  27. linuxDepInfo := &dep.InstallInfo{
  28. Reason: dep.Explicit,
  29. Source: dep.Sync,
  30. AURBase: nil,
  31. LocalVersion: "4.5.0-1",
  32. Version: "5.0.0-1",
  33. SyncDBName: ptrString("core"),
  34. Upgrade: true,
  35. Devel: false,
  36. }
  37. exampleDepInfoDevel := &dep.InstallInfo{
  38. Source: dep.AUR,
  39. Reason: dep.Dep,
  40. AURBase: ptrString("example"),
  41. LocalVersion: "2.2.1.r32.41baa362-1",
  42. Version: "latest-commit",
  43. Upgrade: true,
  44. Devel: true,
  45. }
  46. newDepInfo := &dep.InstallInfo{
  47. Source: dep.Sync,
  48. Reason: dep.Dep,
  49. SyncDBName: ptrString("core"),
  50. Version: "3.0.1-2",
  51. LocalVersion: "",
  52. Upgrade: false,
  53. Devel: false,
  54. }
  55. exampleDepInfoAUR := &dep.InstallInfo{
  56. Source: dep.AUR,
  57. Reason: dep.Dep,
  58. AURBase: ptrString("example"),
  59. LocalVersion: "2.2.1.r32.41baa362-1",
  60. Version: "2.2.1.r69.g8a10460-1",
  61. Upgrade: true,
  62. Devel: false,
  63. }
  64. yayDepInfo := &dep.InstallInfo{
  65. Reason: dep.Explicit,
  66. Source: dep.AUR,
  67. AURBase: ptrString("yay"),
  68. LocalVersion: "10.2.3",
  69. Version: "10.2.4",
  70. Upgrade: true,
  71. Devel: false,
  72. }
  73. coreDB := mock.NewDB("core")
  74. dbExe := &mock.DBExecutor{
  75. InstalledRemotePackageNamesFn: func() []string {
  76. return []string{"yay", "example-git"}
  77. },
  78. InstalledRemotePackagesFn: func() map[string]mock.IPackage {
  79. mapRemote := make(map[string]mock.IPackage)
  80. mapRemote["yay"] = &mock.Package{
  81. PName: "yay",
  82. PBase: "yay",
  83. PVersion: "10.2.3",
  84. PReason: alpm.PkgReasonExplicit,
  85. }
  86. mapRemote["example-git"] = &mock.Package{
  87. PName: "example-git",
  88. PBase: "example",
  89. PVersion: "2.2.1.r32.41baa362-1",
  90. PReason: alpm.PkgReasonDepend,
  91. }
  92. return mapRemote
  93. },
  94. LocalSatisfierExistsFn: func(string) bool { return false },
  95. SyncSatisfierFn: func(s string) mock.IPackage {
  96. return &mock.Package{
  97. PName: "new-dep",
  98. PVersion: "3.0.1-2",
  99. PDB: coreDB,
  100. }
  101. },
  102. SyncUpgradesFn: func(bool) (map[string]db.SyncUpgrade, error) {
  103. mapUpgrades := make(map[string]db.SyncUpgrade)
  104. mapUpgrades["linux"] = db.SyncUpgrade{
  105. Package: &mock.Package{
  106. PName: "linux",
  107. PVersion: "5.0.0-1",
  108. PReason: alpm.PkgReasonDepend,
  109. PDB: coreDB,
  110. PDepends: mock.DependList{Depends: []alpm.Depend{
  111. {Name: "new-dep", Version: "3.0.1"},
  112. }},
  113. },
  114. LocalVersion: "4.5.0-1",
  115. Reason: alpm.PkgReasonExplicit,
  116. }
  117. mapUpgrades["new-dep"] = db.SyncUpgrade{
  118. Package: &mock.Package{
  119. PName: "new-dep",
  120. PVersion: "3.0.1-2",
  121. PReason: alpm.PkgReasonDepend,
  122. PDB: coreDB,
  123. },
  124. LocalVersion: "",
  125. Reason: alpm.PkgReasonDepend,
  126. }
  127. return mapUpgrades, nil
  128. },
  129. ReposFn: func() []string { return []string{"core"} },
  130. }
  131. vcsStore := &vcs.Mock{
  132. ToUpgradeReturn: []string{"example-git"},
  133. }
  134. mockAUR := &mockaur.MockAUR{
  135. GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
  136. return []aur.Pkg{
  137. {Name: "yay", Version: "10.2.4", PackageBase: "yay"},
  138. {
  139. Name: "example-git", Version: "2.2.1.r69.g8a10460-1",
  140. PackageBase: "example", Depends: []string{"new-dep"},
  141. },
  142. }, nil
  143. },
  144. }
  145. type fields struct {
  146. input io.Reader
  147. output io.Writer
  148. noConfirm bool
  149. devel bool
  150. }
  151. type args struct {
  152. graph *topo.Graph[string, *dep.InstallInfo]
  153. enableDowngrade bool
  154. }
  155. tests := []struct {
  156. name string
  157. fields fields
  158. args args
  159. mustExist map[string]*dep.InstallInfo
  160. mustNotExist map[string]bool
  161. wantExclude []string
  162. wantErr bool
  163. }{
  164. {
  165. name: "no input",
  166. fields: fields{
  167. input: strings.NewReader("\n"),
  168. output: io.Discard,
  169. noConfirm: false,
  170. },
  171. args: args{
  172. graph: nil,
  173. enableDowngrade: false,
  174. },
  175. mustExist: map[string]*dep.InstallInfo{
  176. "yay": yayDepInfo,
  177. "linux": linuxDepInfo,
  178. "example-git": exampleDepInfoAUR,
  179. "new-dep": newDepInfo,
  180. },
  181. mustNotExist: map[string]bool{},
  182. wantErr: false,
  183. wantExclude: []string{},
  184. },
  185. {
  186. name: "no input devel",
  187. fields: fields{
  188. input: strings.NewReader("\n"),
  189. output: io.Discard,
  190. noConfirm: false,
  191. devel: true,
  192. },
  193. args: args{
  194. graph: nil,
  195. enableDowngrade: false,
  196. },
  197. mustExist: map[string]*dep.InstallInfo{
  198. "yay": yayDepInfo,
  199. "linux": linuxDepInfo,
  200. "example-git": exampleDepInfoDevel,
  201. },
  202. mustNotExist: map[string]bool{},
  203. wantErr: false,
  204. wantExclude: []string{},
  205. },
  206. {
  207. name: "exclude example-git",
  208. fields: fields{
  209. input: strings.NewReader("2\n"),
  210. output: io.Discard,
  211. noConfirm: false,
  212. },
  213. args: args{
  214. graph: nil,
  215. enableDowngrade: false,
  216. },
  217. mustExist: map[string]*dep.InstallInfo{
  218. "yay": yayDepInfo,
  219. "linux": linuxDepInfo,
  220. },
  221. mustNotExist: map[string]bool{"example-git": true, "new-dep": true},
  222. wantErr: false,
  223. wantExclude: []string{"example-git", "new-dep"},
  224. },
  225. {
  226. name: "exclude new-dep should have no effect",
  227. fields: fields{
  228. input: strings.NewReader("1 3 4\n"),
  229. output: io.Discard,
  230. noConfirm: false,
  231. },
  232. args: args{
  233. graph: nil,
  234. enableDowngrade: false,
  235. },
  236. mustExist: map[string]*dep.InstallInfo{
  237. "example-git": exampleDepInfoAUR,
  238. "new-dep": newDepInfo,
  239. },
  240. mustNotExist: map[string]bool{"linux": true, "yay": true},
  241. wantErr: false,
  242. wantExclude: []string{"linux", "yay"},
  243. },
  244. {
  245. name: "exclude yay",
  246. fields: fields{
  247. input: strings.NewReader("1\n"),
  248. output: io.Discard,
  249. noConfirm: false,
  250. },
  251. args: args{
  252. graph: nil,
  253. enableDowngrade: false,
  254. },
  255. mustExist: map[string]*dep.InstallInfo{
  256. "linux": linuxDepInfo,
  257. "example-git": exampleDepInfoAUR,
  258. },
  259. mustNotExist: map[string]bool{"yay": true},
  260. wantErr: false,
  261. wantExclude: []string{"yay"},
  262. },
  263. {
  264. name: "exclude linux",
  265. fields: fields{
  266. input: strings.NewReader("4\n"),
  267. output: io.Discard,
  268. noConfirm: false,
  269. },
  270. args: args{
  271. graph: nil,
  272. enableDowngrade: false,
  273. },
  274. mustExist: map[string]*dep.InstallInfo{
  275. "yay": yayDepInfo,
  276. "example-git": exampleDepInfoAUR,
  277. "new-dep": newDepInfo,
  278. },
  279. mustNotExist: map[string]bool{"linux": true},
  280. wantErr: false,
  281. wantExclude: []string{"linux"},
  282. },
  283. {
  284. name: "only linux",
  285. fields: fields{
  286. input: strings.NewReader("^4\n"),
  287. output: io.Discard,
  288. noConfirm: false,
  289. },
  290. args: args{
  291. graph: nil,
  292. enableDowngrade: false,
  293. },
  294. mustExist: map[string]*dep.InstallInfo{
  295. "linux": linuxDepInfo,
  296. },
  297. mustNotExist: map[string]bool{"yay": true, "example-git": true},
  298. wantErr: false,
  299. wantExclude: []string{"yay", "example-git", "new-dep"},
  300. },
  301. {
  302. name: "exclude all",
  303. fields: fields{
  304. input: strings.NewReader("1-4\n"),
  305. output: io.Discard,
  306. noConfirm: false,
  307. },
  308. args: args{
  309. graph: nil,
  310. enableDowngrade: false,
  311. },
  312. mustExist: map[string]*dep.InstallInfo{},
  313. mustNotExist: map[string]bool{"yay": true, "example-git": true, "linux": true},
  314. wantErr: false,
  315. wantExclude: []string{"yay", "example-git", "linux", "new-dep"},
  316. },
  317. }
  318. for _, tt := range tests {
  319. t.Run(tt.name, func(t *testing.T) {
  320. grapher := dep.NewGrapher(dbExe, mockAUR,
  321. false, true, false, false, false, text.NewLogger(tt.fields.output,
  322. tt.fields.input, true, "test"))
  323. cfg := &settings.Configuration{
  324. Devel: tt.fields.devel, Mode: parser.ModeAny,
  325. }
  326. logger := text.NewLogger(tt.fields.output,
  327. tt.fields.input, true, "test")
  328. u := &UpgradeService{
  329. log: logger,
  330. grapher: grapher,
  331. aurCache: mockAUR,
  332. dbExecutor: dbExe,
  333. vcsStore: vcsStore,
  334. cfg: cfg,
  335. noConfirm: tt.fields.noConfirm,
  336. AURWarnings: query.NewWarnings(logger),
  337. }
  338. got, err := u.GraphUpgrades(context.Background(), tt.args.graph, tt.args.enableDowngrade, func(*Upgrade) bool { return true })
  339. if (err != nil) != tt.wantErr {
  340. t.Errorf("UpgradeService.GraphUpgrades() error = %v, wantErr %v", err, tt.wantErr)
  341. return
  342. }
  343. excluded, err := u.UserExcludeUpgrades(got)
  344. require.NoError(t, err)
  345. for node, info := range tt.mustExist {
  346. assert.True(t, got.Exists(node), node)
  347. assert.Equal(t, info, got.GetNodeInfo(node).Value)
  348. }
  349. for node := range tt.mustNotExist {
  350. assert.False(t, got.Exists(node), node)
  351. }
  352. assert.ElementsMatch(t, tt.wantExclude, excluded)
  353. })
  354. }
  355. }
  356. func TestUpgradeService_GraphUpgradesNoUpdates(t *testing.T) {
  357. t.Parallel()
  358. dbExe := &mock.DBExecutor{
  359. InstalledRemotePackageNamesFn: func() []string {
  360. return []string{"yay", "example-git"}
  361. },
  362. InstalledRemotePackagesFn: func() map[string]mock.IPackage {
  363. mapRemote := make(map[string]mock.IPackage)
  364. mapRemote["yay"] = &mock.Package{
  365. PName: "yay",
  366. PBase: "yay",
  367. PVersion: "10.2.3",
  368. PReason: alpm.PkgReasonExplicit,
  369. }
  370. mapRemote["example-git"] = &mock.Package{
  371. PName: "example-git",
  372. PBase: "example",
  373. PVersion: "2.2.1.r32.41baa362-1",
  374. PReason: alpm.PkgReasonDepend,
  375. }
  376. return mapRemote
  377. },
  378. SyncUpgradesFn: func(bool) (map[string]db.SyncUpgrade, error) {
  379. mapUpgrades := make(map[string]db.SyncUpgrade)
  380. return mapUpgrades, nil
  381. },
  382. ReposFn: func() []string { return []string{"core"} },
  383. }
  384. vcsStore := &vcs.Mock{
  385. ToUpgradeReturn: []string{},
  386. }
  387. mockAUR := &mockaur.MockAUR{
  388. GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
  389. return []aur.Pkg{}, nil
  390. },
  391. }
  392. type fields struct {
  393. input io.Reader
  394. output io.Writer
  395. noConfirm bool
  396. devel bool
  397. }
  398. type args struct {
  399. graph *topo.Graph[string, *dep.InstallInfo]
  400. enableDowngrade bool
  401. }
  402. tests := []struct {
  403. name string
  404. fields fields
  405. args args
  406. mustExist map[string]*dep.InstallInfo
  407. mustNotExist map[string]bool
  408. wantExclude []string
  409. wantErr bool
  410. }{
  411. {
  412. name: "no input",
  413. fields: fields{
  414. input: strings.NewReader(""),
  415. output: io.Discard,
  416. noConfirm: false,
  417. },
  418. args: args{
  419. graph: nil,
  420. enableDowngrade: false,
  421. },
  422. mustExist: map[string]*dep.InstallInfo{},
  423. mustNotExist: map[string]bool{},
  424. wantErr: false,
  425. wantExclude: []string{},
  426. },
  427. }
  428. for _, tt := range tests {
  429. t.Run(tt.name, func(t *testing.T) {
  430. grapher := dep.NewGrapher(dbExe, mockAUR,
  431. false, true, false, false, false, text.NewLogger(tt.fields.output,
  432. tt.fields.input, true, "test"))
  433. cfg := &settings.Configuration{
  434. Devel: tt.fields.devel,
  435. Mode: parser.ModeAny,
  436. }
  437. logger := text.NewLogger(tt.fields.output,
  438. tt.fields.input, true, "test")
  439. u := &UpgradeService{
  440. log: logger,
  441. grapher: grapher,
  442. aurCache: mockAUR,
  443. dbExecutor: dbExe,
  444. vcsStore: vcsStore,
  445. cfg: cfg,
  446. noConfirm: tt.fields.noConfirm,
  447. AURWarnings: query.NewWarnings(logger),
  448. }
  449. got, err := u.GraphUpgrades(context.Background(), tt.args.graph, tt.args.enableDowngrade, func(*Upgrade) bool { return true })
  450. if (err != nil) != tt.wantErr {
  451. t.Errorf("UpgradeService.GraphUpgrades() error = %v, wantErr %v", err, tt.wantErr)
  452. return
  453. }
  454. excluded, err := u.UserExcludeUpgrades(got)
  455. require.NoError(t, err)
  456. for node, info := range tt.mustExist {
  457. assert.True(t, got.Exists(node), node)
  458. assert.Equal(t, info, got.GetNodeInfo(node).Value)
  459. }
  460. for node := range tt.mustNotExist {
  461. assert.False(t, got.Exists(node), node)
  462. }
  463. assert.ElementsMatch(t, tt.wantExclude, excluded)
  464. })
  465. }
  466. }
  467. func TestUpgradeService_Warnings(t *testing.T) {
  468. t.Parallel()
  469. dbExe := &mock.DBExecutor{
  470. InstalledRemotePackageNamesFn: func() []string {
  471. return []string{"orphan", "outdated", "missing", "orphan-ignored"}
  472. },
  473. InstalledRemotePackagesFn: func() map[string]mock.IPackage {
  474. mapRemote := make(map[string]mock.IPackage)
  475. mapRemote["orphan"] = &mock.Package{
  476. PName: "orphan",
  477. PBase: "orphan",
  478. PVersion: "10.2.3",
  479. PReason: alpm.PkgReasonExplicit,
  480. }
  481. mapRemote["outdated"] = &mock.Package{
  482. PName: "outdated",
  483. PBase: "outdated",
  484. PVersion: "10.2.3",
  485. PReason: alpm.PkgReasonExplicit,
  486. }
  487. mapRemote["missing"] = &mock.Package{
  488. PName: "missing",
  489. PBase: "missing",
  490. PVersion: "10.2.3",
  491. PReason: alpm.PkgReasonExplicit,
  492. }
  493. mapRemote["orphan-ignored"] = &mock.Package{
  494. PName: "orphan-ignored",
  495. PBase: "orphan-ignored",
  496. PVersion: "10.2.3",
  497. PReason: alpm.PkgReasonExplicit,
  498. PShouldIgnore: true,
  499. }
  500. return mapRemote
  501. },
  502. LocalSatisfierExistsFn: func(string) bool { return false },
  503. SyncSatisfierFn: func(s string) mock.IPackage {
  504. return nil
  505. },
  506. SyncUpgradesFn: func(bool) (map[string]db.SyncUpgrade, error) {
  507. mapUpgrades := make(map[string]db.SyncUpgrade)
  508. return mapUpgrades, nil
  509. },
  510. ReposFn: func() []string { return []string{"core"} },
  511. }
  512. vcsStore := &vcs.Mock{
  513. ToUpgradeReturn: []string{},
  514. }
  515. mockAUR := &mockaur.MockAUR{
  516. GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
  517. return []aur.Pkg{
  518. {
  519. Name: "outdated", Version: "10.2.4", PackageBase: "orphan",
  520. OutOfDate: 100, Maintainer: "bob",
  521. },
  522. {
  523. Name: "orphan", Version: "10.2.4", PackageBase: "orphan",
  524. Maintainer: "",
  525. },
  526. }, nil
  527. },
  528. }
  529. logger := text.NewLogger(io.Discard,
  530. strings.NewReader("\n"), true, "test")
  531. grapher := dep.NewGrapher(dbExe, mockAUR,
  532. false, true, false, false, false, logger)
  533. cfg := &settings.Configuration{
  534. Devel: false, Mode: parser.ModeAUR,
  535. }
  536. u := &UpgradeService{
  537. log: logger,
  538. grapher: grapher,
  539. aurCache: mockAUR,
  540. dbExecutor: dbExe,
  541. vcsStore: vcsStore,
  542. cfg: cfg,
  543. noConfirm: true,
  544. AURWarnings: query.NewWarnings(logger),
  545. }
  546. _, err := u.GraphUpgrades(context.Background(), nil, false, func(*Upgrade) bool { return true })
  547. require.NoError(t, err)
  548. assert.Equal(t, []string{"missing"}, u.AURWarnings.Missing)
  549. assert.Equal(t, []string{"outdated"}, u.AURWarnings.OutOfDate)
  550. assert.Equal(t, []string{"orphan"}, u.AURWarnings.Orphans)
  551. }