local_install_test.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447
  1. package main
  2. import (
  3. "context"
  4. "fmt"
  5. "io"
  6. "os"
  7. "os/exec"
  8. "strings"
  9. "sync"
  10. "testing"
  11. aur "github.com/Jguer/aur"
  12. "github.com/stretchr/testify/assert"
  13. "github.com/stretchr/testify/require"
  14. "github.com/Jguer/yay/v11/pkg/db/mock"
  15. mockaur "github.com/Jguer/yay/v11/pkg/dep/mock"
  16. "github.com/Jguer/yay/v11/pkg/settings"
  17. "github.com/Jguer/yay/v11/pkg/settings/exe"
  18. "github.com/Jguer/yay/v11/pkg/settings/parser"
  19. "github.com/Jguer/yay/v11/pkg/text"
  20. "github.com/Jguer/yay/v11/pkg/vcs"
  21. )
  22. func TestIntegrationLocalInstall(t *testing.T) {
  23. makepkgBin := t.TempDir() + "/makepkg"
  24. pacmanBin := t.TempDir() + "/pacman"
  25. gitBin := t.TempDir() + "/git"
  26. tmpDir := t.TempDir()
  27. f, err := os.OpenFile(makepkgBin, os.O_RDONLY|os.O_CREATE, 0o755)
  28. require.NoError(t, err)
  29. require.NoError(t, f.Close())
  30. f, err = os.OpenFile(pacmanBin, os.O_RDONLY|os.O_CREATE, 0o755)
  31. require.NoError(t, err)
  32. require.NoError(t, f.Close())
  33. f, err = os.OpenFile(gitBin, os.O_RDONLY|os.O_CREATE, 0o755)
  34. require.NoError(t, err)
  35. require.NoError(t, f.Close())
  36. tars := []string{
  37. tmpDir + "/jellyfin-10.8.4-1-x86_64.pkg.tar.zst",
  38. tmpDir + "/jellyfin-web-10.8.4-1-x86_64.pkg.tar.zst",
  39. tmpDir + "/jellyfin-server-10.8.4-1-x86_64.pkg.tar.zst",
  40. }
  41. wantShow := []string{
  42. "makepkg --verifysource -Ccf",
  43. "pacman -S --config /etc/pacman.conf -- community/dotnet-sdk-6.0 community/dotnet-runtime-6.0",
  44. "pacman -D -q --asdeps --config /etc/pacman.conf -- dotnet-runtime-6.0 dotnet-sdk-6.0",
  45. "makepkg --nobuild -fC --ignorearch",
  46. "makepkg -c --nobuild --noextract --ignorearch",
  47. "makepkg --nobuild -fC --ignorearch",
  48. "makepkg -c --nobuild --noextract --ignorearch",
  49. "makepkg --nobuild -fC --ignorearch",
  50. "makepkg -c --nobuild --noextract --ignorearch",
  51. "pacman -U --config /etc/pacman.conf -- /testdir/jellyfin-server-10.8.4-1-x86_64.pkg.tar.zst /testdir/jellyfin-10.8.4-1-x86_64.pkg.tar.zst /testdir/jellyfin-web-10.8.4-1-x86_64.pkg.tar.zst",
  52. "pacman -D -q --asexplicit --config /etc/pacman.conf -- jellyfin-server jellyfin jellyfin-web",
  53. }
  54. wantCapture := []string{
  55. "makepkg --packagelist",
  56. "git -C testdata/jfin git reset --hard HEAD",
  57. "git -C testdata/jfin git merge --no-edit --ff",
  58. "makepkg --packagelist",
  59. "makepkg --packagelist",
  60. }
  61. captureOverride := func(cmd *exec.Cmd) (stdout string, stderr string, err error) {
  62. return strings.Join(tars, "\n"), "", nil
  63. }
  64. once := sync.Once{}
  65. showOverride := func(cmd *exec.Cmd) error {
  66. once.Do(func() {
  67. for _, tar := range tars {
  68. f, err := os.OpenFile(tar, os.O_RDONLY|os.O_CREATE, 0o666)
  69. require.NoError(t, err)
  70. require.NoError(t, f.Close())
  71. }
  72. })
  73. return nil
  74. }
  75. mockRunner := &exe.MockRunner{CaptureFn: captureOverride, ShowFn: showOverride}
  76. cmdBuilder := &exe.CmdBuilder{
  77. MakepkgBin: makepkgBin,
  78. SudoBin: "su",
  79. PacmanBin: pacmanBin,
  80. PacmanConfigPath: "/etc/pacman.conf",
  81. GitBin: "git",
  82. Runner: mockRunner,
  83. SudoLoopEnabled: false,
  84. }
  85. cmdArgs := parser.MakeArguments()
  86. cmdArgs.AddArg("B")
  87. cmdArgs.AddArg("i")
  88. cmdArgs.AddTarget("testdata/jfin")
  89. settings.NoConfirm = true
  90. defer func() { settings.NoConfirm = false }()
  91. db := &mock.DBExecutor{
  92. AlpmArchitecturesFn: func() ([]string, error) {
  93. return []string{"x86_64"}, nil
  94. },
  95. LocalSatisfierExistsFn: func(s string) bool {
  96. switch s {
  97. case "dotnet-sdk>=6", "dotnet-sdk<7", "dotnet-runtime>=6", "dotnet-runtime<7", "jellyfin-server=10.8.4", "jellyfin-web=10.8.4":
  98. return false
  99. }
  100. return true
  101. },
  102. SyncSatisfierFn: func(s string) mock.IPackage {
  103. switch s {
  104. case "dotnet-runtime>=6", "dotnet-runtime<7":
  105. return &mock.Package{
  106. PName: "dotnet-runtime-6.0",
  107. PBase: "dotnet-runtime-6.0",
  108. PVersion: "6.0.100-1",
  109. PDB: mock.NewDB("community"),
  110. }
  111. case "dotnet-sdk>=6", "dotnet-sdk<7":
  112. return &mock.Package{
  113. PName: "dotnet-sdk-6.0",
  114. PBase: "dotnet-sdk-6.0",
  115. PVersion: "6.0.100-1",
  116. PDB: mock.NewDB("community"),
  117. }
  118. }
  119. return nil
  120. },
  121. }
  122. config := &settings.Configuration{
  123. RemoveMake: "no",
  124. Runtime: &settings.Runtime{
  125. Logger: text.NewLogger(io.Discard, strings.NewReader(""), true, "test"),
  126. CmdBuilder: cmdBuilder,
  127. VCSStore: &vcs.Mock{},
  128. AURCache: &mockaur.MockAUR{
  129. GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
  130. return []aur.Pkg{}, nil
  131. },
  132. },
  133. },
  134. }
  135. err = handleCmd(context.Background(), config, cmdArgs, db)
  136. require.NoError(t, err)
  137. require.Len(t, mockRunner.ShowCalls, len(wantShow))
  138. require.Len(t, mockRunner.CaptureCalls, len(wantCapture))
  139. for i, call := range mockRunner.ShowCalls {
  140. show := call.Args[0].(*exec.Cmd).String()
  141. show = strings.ReplaceAll(show, tmpDir, "/testdir") // replace the temp dir with a static path
  142. show = strings.ReplaceAll(show, makepkgBin, "makepkg")
  143. show = strings.ReplaceAll(show, pacmanBin, "pacman")
  144. show = strings.ReplaceAll(show, gitBin, "pacman")
  145. // options are in a different order on different systems and on CI root user is used
  146. assert.Subset(t, strings.Split(show, " "), strings.Split(wantShow[i], " "), fmt.Sprintf("%d - %s", i, show))
  147. }
  148. }
  149. func TestIntegrationLocalInstallMissingDep(t *testing.T) {
  150. wantErr := "could not find dotnet-sdk<7"
  151. makepkgBin := t.TempDir() + "/makepkg"
  152. pacmanBin := t.TempDir() + "/pacman"
  153. gitBin := t.TempDir() + "/git"
  154. tmpDir := t.TempDir()
  155. f, err := os.OpenFile(makepkgBin, os.O_RDONLY|os.O_CREATE, 0o755)
  156. require.NoError(t, err)
  157. require.NoError(t, f.Close())
  158. f, err = os.OpenFile(pacmanBin, os.O_RDONLY|os.O_CREATE, 0o755)
  159. require.NoError(t, err)
  160. require.NoError(t, f.Close())
  161. f, err = os.OpenFile(gitBin, os.O_RDONLY|os.O_CREATE, 0o755)
  162. require.NoError(t, err)
  163. require.NoError(t, f.Close())
  164. tars := []string{
  165. tmpDir + "/jellyfin-10.8.4-1-x86_64.pkg.tar.zst",
  166. tmpDir + "/jellyfin-web-10.8.4-1-x86_64.pkg.tar.zst",
  167. tmpDir + "/jellyfin-server-10.8.4-1-x86_64.pkg.tar.zst",
  168. }
  169. wantShow := []string{}
  170. wantCapture := []string{}
  171. captureOverride := func(cmd *exec.Cmd) (stdout string, stderr string, err error) {
  172. return strings.Join(tars, "\n"), "", nil
  173. }
  174. once := sync.Once{}
  175. showOverride := func(cmd *exec.Cmd) error {
  176. once.Do(func() {
  177. for _, tar := range tars {
  178. f, err := os.OpenFile(tar, os.O_RDONLY|os.O_CREATE, 0o666)
  179. require.NoError(t, err)
  180. require.NoError(t, f.Close())
  181. }
  182. })
  183. return nil
  184. }
  185. mockRunner := &exe.MockRunner{CaptureFn: captureOverride, ShowFn: showOverride}
  186. cmdBuilder := &exe.CmdBuilder{
  187. MakepkgBin: makepkgBin,
  188. SudoBin: "su",
  189. PacmanBin: pacmanBin,
  190. PacmanConfigPath: "/etc/pacman.conf",
  191. GitBin: "git",
  192. Runner: mockRunner,
  193. SudoLoopEnabled: false,
  194. }
  195. cmdArgs := parser.MakeArguments()
  196. cmdArgs.AddArg("B")
  197. cmdArgs.AddArg("i")
  198. cmdArgs.AddTarget("testdata/jfin")
  199. settings.NoConfirm = true
  200. defer func() { settings.NoConfirm = false }()
  201. db := &mock.DBExecutor{
  202. AlpmArchitecturesFn: func() ([]string, error) {
  203. return []string{"x86_64"}, nil
  204. },
  205. LocalSatisfierExistsFn: func(s string) bool {
  206. switch s {
  207. case "dotnet-sdk>=6", "dotnet-sdk<7", "dotnet-runtime>=6", "dotnet-runtime<7", "jellyfin-server=10.8.4", "jellyfin-web=10.8.4":
  208. return false
  209. }
  210. return true
  211. },
  212. SyncSatisfierFn: func(s string) mock.IPackage {
  213. switch s {
  214. case "dotnet-runtime>=6", "dotnet-runtime<7":
  215. return &mock.Package{
  216. PName: "dotnet-runtime-6.0",
  217. PBase: "dotnet-runtime-6.0",
  218. PVersion: "6.0.100-1",
  219. PDB: mock.NewDB("community"),
  220. }
  221. }
  222. return nil
  223. },
  224. }
  225. config := &settings.Configuration{
  226. Runtime: &settings.Runtime{
  227. Logger: text.NewLogger(io.Discard, strings.NewReader(""), true, "test"),
  228. CmdBuilder: cmdBuilder,
  229. VCSStore: &vcs.Mock{},
  230. AURCache: &mockaur.MockAUR{
  231. GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
  232. return []aur.Pkg{}, nil
  233. },
  234. },
  235. },
  236. }
  237. err = handleCmd(context.Background(), config, cmdArgs, db)
  238. require.Error(t, err)
  239. require.EqualError(t, err, wantErr)
  240. require.Len(t, mockRunner.ShowCalls, len(wantShow))
  241. require.Len(t, mockRunner.CaptureCalls, len(wantCapture))
  242. for i, call := range mockRunner.ShowCalls {
  243. show := call.Args[0].(*exec.Cmd).String()
  244. show = strings.ReplaceAll(show, tmpDir, "/testdir") // replace the temp dir with a static path
  245. show = strings.ReplaceAll(show, makepkgBin, "makepkg")
  246. show = strings.ReplaceAll(show, pacmanBin, "pacman")
  247. show = strings.ReplaceAll(show, gitBin, "pacman")
  248. // options are in a different order on different systems and on CI root user is used
  249. assert.Subset(t, strings.Split(show, " "), strings.Split(wantShow[i], " "), fmt.Sprintf("%d - %s", i, show))
  250. }
  251. }
  252. func TestIntegrationLocalInstallNeeded(t *testing.T) {
  253. makepkgBin := t.TempDir() + "/makepkg"
  254. pacmanBin := t.TempDir() + "/pacman"
  255. gitBin := t.TempDir() + "/git"
  256. tmpDir := t.TempDir()
  257. f, err := os.OpenFile(makepkgBin, os.O_RDONLY|os.O_CREATE, 0o755)
  258. require.NoError(t, err)
  259. require.NoError(t, f.Close())
  260. f, err = os.OpenFile(pacmanBin, os.O_RDONLY|os.O_CREATE, 0o755)
  261. require.NoError(t, err)
  262. require.NoError(t, f.Close())
  263. f, err = os.OpenFile(gitBin, os.O_RDONLY|os.O_CREATE, 0o755)
  264. require.NoError(t, err)
  265. require.NoError(t, f.Close())
  266. tars := []string{
  267. tmpDir + "/jellyfin-10.8.4-1-x86_64.pkg.tar.zst",
  268. tmpDir + "/jellyfin-web-10.8.4-1-x86_64.pkg.tar.zst",
  269. tmpDir + "/jellyfin-server-10.8.4-1-x86_64.pkg.tar.zst",
  270. }
  271. wantShow := []string{
  272. "makepkg --verifysource -Ccf",
  273. "pacman -S --config /etc/pacman.conf -- community/dotnet-sdk-6.0 community/dotnet-runtime-6.0",
  274. "pacman -D -q --asdeps --config /etc/pacman.conf -- dotnet-runtime-6.0 dotnet-sdk-6.0",
  275. "makepkg --nobuild -fC --ignorearch",
  276. "makepkg -c --nobuild --noextract --ignorearch",
  277. "makepkg --nobuild -fC --ignorearch",
  278. "makepkg -c --nobuild --noextract --ignorearch",
  279. "makepkg --nobuild -fC --ignorearch",
  280. "makepkg -c --nobuild --noextract --ignorearch",
  281. }
  282. wantCapture := []string{
  283. "makepkg --packagelist",
  284. "git -C testdata/jfin git reset --hard HEAD",
  285. "git -C testdata/jfin git merge --no-edit --ff",
  286. "makepkg --packagelist",
  287. "makepkg --packagelist",
  288. }
  289. captureOverride := func(cmd *exec.Cmd) (stdout string, stderr string, err error) {
  290. return strings.Join(tars, "\n"), "", nil
  291. }
  292. once := sync.Once{}
  293. showOverride := func(cmd *exec.Cmd) error {
  294. once.Do(func() {
  295. for _, tar := range tars {
  296. f, err := os.OpenFile(tar, os.O_RDONLY|os.O_CREATE, 0o666)
  297. require.NoError(t, err)
  298. require.NoError(t, f.Close())
  299. }
  300. })
  301. return nil
  302. }
  303. mockRunner := &exe.MockRunner{CaptureFn: captureOverride, ShowFn: showOverride}
  304. cmdBuilder := &exe.CmdBuilder{
  305. MakepkgBin: makepkgBin,
  306. SudoBin: "su",
  307. PacmanBin: pacmanBin,
  308. PacmanConfigPath: "/etc/pacman.conf",
  309. GitBin: "git",
  310. Runner: mockRunner,
  311. SudoLoopEnabled: false,
  312. }
  313. cmdArgs := parser.MakeArguments()
  314. cmdArgs.AddArg("B")
  315. cmdArgs.AddArg("i")
  316. cmdArgs.AddArg("needed")
  317. cmdArgs.AddTarget("testdata/jfin")
  318. settings.NoConfirm = true
  319. defer func() { settings.NoConfirm = false }()
  320. db := &mock.DBExecutor{
  321. AlpmArchitecturesFn: func() ([]string, error) {
  322. return []string{"x86_64"}, nil
  323. },
  324. IsCorrectVersionInstalledFn: func(s1, s2 string) bool {
  325. return true
  326. },
  327. LocalPackageFn: func(s string) mock.IPackage {
  328. if s == "jellyfin-server" {
  329. return &mock.Package{
  330. PName: "jellyfin-server",
  331. PBase: "jellyfin-server",
  332. PVersion: "10.8.4-1",
  333. PDB: mock.NewDB("community"),
  334. }
  335. }
  336. return nil
  337. },
  338. LocalSatisfierExistsFn: func(s string) bool {
  339. switch s {
  340. case "dotnet-sdk>=6", "dotnet-sdk<7", "dotnet-runtime>=6", "dotnet-runtime<7", "jellyfin-server=10.8.4", "jellyfin-web=10.8.4":
  341. return false
  342. }
  343. return true
  344. },
  345. SyncSatisfierFn: func(s string) mock.IPackage {
  346. switch s {
  347. case "dotnet-runtime>=6", "dotnet-runtime<7":
  348. return &mock.Package{
  349. PName: "dotnet-runtime-6.0",
  350. PBase: "dotnet-runtime-6.0",
  351. PVersion: "6.0.100-1",
  352. PDB: mock.NewDB("community"),
  353. }
  354. case "dotnet-sdk>=6", "dotnet-sdk<7":
  355. return &mock.Package{
  356. PName: "dotnet-sdk-6.0",
  357. PBase: "dotnet-sdk-6.0",
  358. PVersion: "6.0.100-1",
  359. PDB: mock.NewDB("community"),
  360. }
  361. }
  362. return nil
  363. },
  364. }
  365. config := &settings.Configuration{
  366. RemoveMake: "no",
  367. Runtime: &settings.Runtime{
  368. Logger: text.NewLogger(io.Discard, strings.NewReader(""), true, "test"),
  369. CmdBuilder: cmdBuilder,
  370. VCSStore: &vcs.Mock{},
  371. AURCache: &mockaur.MockAUR{
  372. GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
  373. return []aur.Pkg{}, nil
  374. },
  375. },
  376. },
  377. }
  378. err = handleCmd(context.Background(), config, cmdArgs, db)
  379. require.NoError(t, err)
  380. require.Len(t, mockRunner.ShowCalls, len(wantShow), "show calls: %v", mockRunner.ShowCalls)
  381. require.Len(t, mockRunner.CaptureCalls, len(wantCapture))
  382. for i, call := range mockRunner.ShowCalls {
  383. show := call.Args[0].(*exec.Cmd).String()
  384. show = strings.ReplaceAll(show, tmpDir, "/testdir") // replace the temp dir with a static path
  385. show = strings.ReplaceAll(show, makepkgBin, "makepkg")
  386. show = strings.ReplaceAll(show, pacmanBin, "pacman")
  387. show = strings.ReplaceAll(show, gitBin, "pacman")
  388. // options are in a different order on different systems and on CI root user is used
  389. assert.Subset(t, strings.Split(show, " "), strings.Split(wantShow[i], " "), fmt.Sprintf("%d - %s", i, show))
  390. }
  391. }