local_install_test.go 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029
  1. //go:build !integration
  2. // +build !integration
  3. package main
  4. import (
  5. "context"
  6. "fmt"
  7. "io"
  8. "os"
  9. "os/exec"
  10. "path/filepath"
  11. "strings"
  12. "sync"
  13. "testing"
  14. aur "github.com/Jguer/aur"
  15. "github.com/stretchr/testify/assert"
  16. "github.com/stretchr/testify/require"
  17. "github.com/Jguer/yay/v12/pkg/db/mock"
  18. "github.com/Jguer/yay/v12/pkg/dep"
  19. mockaur "github.com/Jguer/yay/v12/pkg/dep/mock"
  20. "github.com/Jguer/yay/v12/pkg/runtime"
  21. "github.com/Jguer/yay/v12/pkg/settings"
  22. "github.com/Jguer/yay/v12/pkg/settings/exe"
  23. "github.com/Jguer/yay/v12/pkg/settings/parser"
  24. "github.com/Jguer/yay/v12/pkg/text"
  25. "github.com/Jguer/yay/v12/pkg/vcs"
  26. )
  27. func newTestLogger() *text.Logger {
  28. return text.NewLogger(io.Discard, io.Discard, strings.NewReader(""), true, "test")
  29. }
  30. func TestIntegrationLocalInstall(t *testing.T) {
  31. makepkgBin := t.TempDir() + "/makepkg"
  32. pacmanBin := t.TempDir() + "/pacman"
  33. gitBin := t.TempDir() + "/git"
  34. tmpDir := t.TempDir()
  35. f, err := os.OpenFile(makepkgBin, os.O_RDONLY|os.O_CREATE, 0o755)
  36. require.NoError(t, err)
  37. require.NoError(t, f.Close())
  38. f, err = os.OpenFile(pacmanBin, os.O_RDONLY|os.O_CREATE, 0o755)
  39. require.NoError(t, err)
  40. require.NoError(t, f.Close())
  41. f, err = os.OpenFile(gitBin, os.O_RDONLY|os.O_CREATE, 0o755)
  42. require.NoError(t, err)
  43. require.NoError(t, f.Close())
  44. tars := []string{
  45. tmpDir + "/jellyfin-10.8.4-1-x86_64.pkg.tar.zst",
  46. tmpDir + "/jellyfin-web-10.8.4-1-x86_64.pkg.tar.zst",
  47. tmpDir + "/jellyfin-server-10.8.4-1-x86_64.pkg.tar.zst",
  48. }
  49. wantShow := []string{
  50. "makepkg --verifysource --skippgpcheck -Ccf",
  51. "pacman -S --config /etc/pacman.conf -- community/dotnet-sdk-6.0 community/dotnet-runtime-6.0",
  52. "pacman -D -q --asdeps --config /etc/pacman.conf -- dotnet-runtime-6.0 dotnet-sdk-6.0",
  53. "makepkg --nobuild -fC --ignorearch",
  54. "makepkg -c --nobuild --noextract --ignorearch",
  55. "makepkg --nobuild -fC --ignorearch",
  56. "makepkg -c --nobuild --noextract --ignorearch",
  57. "pacman -U --config /etc/pacman.conf -- /testdir/jellyfin-server-10.8.4-1-x86_64.pkg.tar.zst /testdir/jellyfin-web-10.8.4-1-x86_64.pkg.tar.zst",
  58. "pacman -D -q --asexplicit --config /etc/pacman.conf -- jellyfin-server jellyfin-web",
  59. "makepkg --nobuild -fC --ignorearch",
  60. "makepkg -c --nobuild --noextract --ignorearch",
  61. "pacman -U --config /etc/pacman.conf -- /testdir/jellyfin-10.8.4-1-x86_64.pkg.tar.zst",
  62. "pacman -D -q --asexplicit --config /etc/pacman.conf -- jellyfin",
  63. }
  64. wantCapture := []string{
  65. "makepkg --packagelist",
  66. "git -C testdata/jfin git reset --hard HEAD",
  67. "git -C testdata/jfin git merge --no-edit --ff",
  68. "makepkg --packagelist",
  69. "makepkg --packagelist",
  70. }
  71. captureOverride := func(cmd *exec.Cmd) (stdout string, stderr string, err error) {
  72. return strings.Join(tars, "\n"), "", nil
  73. }
  74. once := sync.Once{}
  75. showOverride := func(cmd *exec.Cmd) error {
  76. once.Do(func() {
  77. for _, tar := range tars {
  78. f, err := os.OpenFile(tar, os.O_RDONLY|os.O_CREATE, 0o666)
  79. require.NoError(t, err)
  80. require.NoError(t, f.Close())
  81. }
  82. })
  83. return nil
  84. }
  85. mockRunner := &exe.MockRunner{CaptureFn: captureOverride, ShowFn: showOverride}
  86. cmdBuilder := &exe.CmdBuilder{
  87. MakepkgBin: makepkgBin,
  88. SudoBin: "su",
  89. PacmanBin: pacmanBin,
  90. PacmanConfigPath: "/etc/pacman.conf",
  91. GitBin: "git",
  92. Runner: mockRunner,
  93. SudoLoopEnabled: false,
  94. }
  95. cmdArgs := parser.MakeArguments()
  96. cmdArgs.AddArg("B")
  97. cmdArgs.AddArg("i")
  98. cmdArgs.AddTarget("testdata/jfin")
  99. settings.NoConfirm = true
  100. defer func() { settings.NoConfirm = false }()
  101. db := &mock.DBExecutor{
  102. AlpmArchitecturesFn: func() ([]string, error) {
  103. return []string{"x86_64"}, nil
  104. },
  105. LocalSatisfierExistsFn: func(s string) bool {
  106. switch s {
  107. case "dotnet-sdk>=6", "dotnet-sdk<7", "dotnet-runtime>=6", "dotnet-runtime<7", "jellyfin-server=10.8.4", "jellyfin-web=10.8.4":
  108. return false
  109. }
  110. return true
  111. },
  112. SyncSatisfierFn: func(s string) mock.IPackage {
  113. switch s {
  114. case "dotnet-runtime>=6", "dotnet-runtime<7":
  115. return &mock.Package{
  116. PName: "dotnet-runtime-6.0",
  117. PBase: "dotnet-runtime-6.0",
  118. PVersion: "6.0.100-1",
  119. PDB: mock.NewDB("community"),
  120. }
  121. case "dotnet-sdk>=6", "dotnet-sdk<7":
  122. return &mock.Package{
  123. PName: "dotnet-sdk-6.0",
  124. PBase: "dotnet-sdk-6.0",
  125. PVersion: "6.0.100-1",
  126. PDB: mock.NewDB("community"),
  127. }
  128. }
  129. return nil
  130. },
  131. LocalPackageFn: func(s string) mock.IPackage { return nil },
  132. InstalledRemotePackageNamesFn: func() []string { return []string{} },
  133. }
  134. run := &runtime.Runtime{
  135. Cfg: &settings.Configuration{
  136. RemoveMake: "no",
  137. },
  138. Logger: newTestLogger(),
  139. CmdBuilder: cmdBuilder,
  140. VCSStore: &vcs.Mock{},
  141. AURClient: &mockaur.MockAUR{
  142. GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
  143. return []aur.Pkg{}, nil
  144. },
  145. },
  146. }
  147. err = handleCmd(context.Background(), run, cmdArgs, db)
  148. require.NoError(t, err)
  149. require.Len(t, mockRunner.ShowCalls, len(wantShow))
  150. require.Len(t, mockRunner.CaptureCalls, len(wantCapture))
  151. for i, call := range mockRunner.ShowCalls {
  152. show := call.Args[0].(*exec.Cmd).String()
  153. show = strings.ReplaceAll(show, tmpDir, "/testdir") // replace the temp dir with a static path
  154. show = strings.ReplaceAll(show, makepkgBin, "makepkg")
  155. show = strings.ReplaceAll(show, pacmanBin, "pacman")
  156. show = strings.ReplaceAll(show, gitBin, "pacman")
  157. // options are in a different order on different systems and on CI root user is used
  158. assert.Subset(t, strings.Split(show, " "), strings.Split(wantShow[i], " "), fmt.Sprintf("%d - %s", i, show))
  159. }
  160. }
  161. func TestIntegrationLocalInstallMissingDep(t *testing.T) {
  162. wantErr := ErrPackagesNotFound
  163. makepkgBin := t.TempDir() + "/makepkg"
  164. pacmanBin := t.TempDir() + "/pacman"
  165. gitBin := t.TempDir() + "/git"
  166. tmpDir := t.TempDir()
  167. f, err := os.OpenFile(makepkgBin, os.O_RDONLY|os.O_CREATE, 0o755)
  168. require.NoError(t, err)
  169. require.NoError(t, f.Close())
  170. f, err = os.OpenFile(pacmanBin, os.O_RDONLY|os.O_CREATE, 0o755)
  171. require.NoError(t, err)
  172. require.NoError(t, f.Close())
  173. f, err = os.OpenFile(gitBin, os.O_RDONLY|os.O_CREATE, 0o755)
  174. require.NoError(t, err)
  175. require.NoError(t, f.Close())
  176. tars := []string{
  177. tmpDir + "/jellyfin-10.8.4-1-x86_64.pkg.tar.zst",
  178. tmpDir + "/jellyfin-web-10.8.4-1-x86_64.pkg.tar.zst",
  179. tmpDir + "/jellyfin-server-10.8.4-1-x86_64.pkg.tar.zst",
  180. }
  181. wantShow := []string{}
  182. wantCapture := []string{}
  183. captureOverride := func(cmd *exec.Cmd) (stdout string, stderr string, err error) {
  184. return strings.Join(tars, "\n"), "", nil
  185. }
  186. once := sync.Once{}
  187. showOverride := func(cmd *exec.Cmd) error {
  188. once.Do(func() {
  189. for _, tar := range tars {
  190. f, err := os.OpenFile(tar, os.O_RDONLY|os.O_CREATE, 0o666)
  191. require.NoError(t, err)
  192. require.NoError(t, f.Close())
  193. }
  194. })
  195. return nil
  196. }
  197. mockRunner := &exe.MockRunner{CaptureFn: captureOverride, ShowFn: showOverride}
  198. cmdBuilder := &exe.CmdBuilder{
  199. MakepkgBin: makepkgBin,
  200. SudoBin: "su",
  201. PacmanBin: pacmanBin,
  202. PacmanConfigPath: "/etc/pacman.conf",
  203. GitBin: "git",
  204. Runner: mockRunner,
  205. SudoLoopEnabled: false,
  206. }
  207. cmdArgs := parser.MakeArguments()
  208. cmdArgs.AddArg("B")
  209. cmdArgs.AddArg("i")
  210. cmdArgs.AddTarget("testdata/jfin")
  211. settings.NoConfirm = true
  212. defer func() { settings.NoConfirm = false }()
  213. db := &mock.DBExecutor{
  214. AlpmArchitecturesFn: func() ([]string, error) {
  215. return []string{"x86_64"}, nil
  216. },
  217. LocalSatisfierExistsFn: func(s string) bool {
  218. switch s {
  219. case "dotnet-sdk>=6", "dotnet-sdk<7", "dotnet-runtime>=6", "dotnet-runtime<7", "jellyfin-server=10.8.4", "jellyfin-web=10.8.4":
  220. return false
  221. }
  222. return true
  223. },
  224. SyncSatisfierFn: func(s string) mock.IPackage {
  225. switch s {
  226. case "dotnet-runtime>=6", "dotnet-runtime<7":
  227. return &mock.Package{
  228. PName: "dotnet-runtime-6.0",
  229. PBase: "dotnet-runtime-6.0",
  230. PVersion: "6.0.100-1",
  231. PDB: mock.NewDB("community"),
  232. }
  233. }
  234. return nil
  235. },
  236. LocalPackageFn: func(string) mock.IPackage { return nil },
  237. }
  238. run := &runtime.Runtime{
  239. Cfg: &settings.Configuration{},
  240. Logger: newTestLogger(),
  241. CmdBuilder: cmdBuilder,
  242. VCSStore: &vcs.Mock{},
  243. AURClient: &mockaur.MockAUR{
  244. GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
  245. return []aur.Pkg{}, nil
  246. },
  247. },
  248. }
  249. err = handleCmd(context.Background(), run, cmdArgs, db)
  250. require.ErrorContains(t, err, wantErr.Error())
  251. require.Len(t, mockRunner.ShowCalls, len(wantShow))
  252. require.Len(t, mockRunner.CaptureCalls, len(wantCapture))
  253. for i, call := range mockRunner.ShowCalls {
  254. show := call.Args[0].(*exec.Cmd).String()
  255. show = strings.ReplaceAll(show, tmpDir, "/testdir") // replace the temp dir with a static path
  256. show = strings.ReplaceAll(show, makepkgBin, "makepkg")
  257. show = strings.ReplaceAll(show, pacmanBin, "pacman")
  258. show = strings.ReplaceAll(show, gitBin, "pacman")
  259. // options are in a different order on different systems and on CI root user is used
  260. assert.Subset(t, strings.Split(show, " "), strings.Split(wantShow[i], " "), fmt.Sprintf("%d - %s", i, show))
  261. }
  262. }
  263. func TestIntegrationLocalInstallNeeded(t *testing.T) {
  264. makepkgBin := t.TempDir() + "/makepkg"
  265. pacmanBin := t.TempDir() + "/pacman"
  266. gitBin := t.TempDir() + "/git"
  267. tmpDir := t.TempDir()
  268. f, err := os.OpenFile(makepkgBin, os.O_RDONLY|os.O_CREATE, 0o755)
  269. require.NoError(t, err)
  270. require.NoError(t, f.Close())
  271. f, err = os.OpenFile(pacmanBin, os.O_RDONLY|os.O_CREATE, 0o755)
  272. require.NoError(t, err)
  273. require.NoError(t, f.Close())
  274. f, err = os.OpenFile(gitBin, os.O_RDONLY|os.O_CREATE, 0o755)
  275. require.NoError(t, err)
  276. require.NoError(t, f.Close())
  277. tars := []string{
  278. tmpDir + "/jellyfin-10.8.4-1-x86_64.pkg.tar.zst",
  279. tmpDir + "/jellyfin-web-10.8.4-1-x86_64.pkg.tar.zst",
  280. tmpDir + "/jellyfin-server-10.8.4-1-x86_64.pkg.tar.zst",
  281. }
  282. wantShow := []string{
  283. "makepkg --verifysource --skippgpcheck -Ccf",
  284. "pacman -S --config /etc/pacman.conf -- community/dotnet-sdk-6.0 community/dotnet-runtime-6.0",
  285. "pacman -D -q --asdeps --config /etc/pacman.conf -- dotnet-runtime-6.0 dotnet-sdk-6.0",
  286. "makepkg --nobuild -fC --ignorearch",
  287. "makepkg -c --nobuild --noextract --ignorearch",
  288. "makepkg --nobuild -fC --ignorearch",
  289. "makepkg -c --nobuild --noextract --ignorearch",
  290. "makepkg --nobuild -fC --ignorearch",
  291. "makepkg -c --nobuild --noextract --ignorearch",
  292. }
  293. wantCapture := []string{
  294. "makepkg --packagelist",
  295. "git -C testdata/jfin git reset --hard HEAD",
  296. "git -C testdata/jfin git merge --no-edit --ff",
  297. "makepkg --packagelist",
  298. "makepkg --packagelist",
  299. }
  300. captureOverride := func(cmd *exec.Cmd) (stdout string, stderr string, err error) {
  301. return strings.Join(tars, "\n"), "", nil
  302. }
  303. once := sync.Once{}
  304. showOverride := func(cmd *exec.Cmd) error {
  305. once.Do(func() {
  306. for _, tar := range tars {
  307. f, err := os.OpenFile(tar, os.O_RDONLY|os.O_CREATE, 0o666)
  308. require.NoError(t, err)
  309. require.NoError(t, f.Close())
  310. }
  311. })
  312. return nil
  313. }
  314. mockRunner := &exe.MockRunner{CaptureFn: captureOverride, ShowFn: showOverride}
  315. cmdBuilder := &exe.CmdBuilder{
  316. MakepkgBin: makepkgBin,
  317. SudoBin: "su",
  318. PacmanBin: pacmanBin,
  319. PacmanConfigPath: "/etc/pacman.conf",
  320. GitBin: "git",
  321. Runner: mockRunner,
  322. SudoLoopEnabled: false,
  323. }
  324. cmdArgs := parser.MakeArguments()
  325. cmdArgs.AddArg("B")
  326. cmdArgs.AddArg("i")
  327. cmdArgs.AddArg("needed")
  328. cmdArgs.AddTarget("testdata/jfin")
  329. settings.NoConfirm = true
  330. defer func() { settings.NoConfirm = false }()
  331. db := &mock.DBExecutor{
  332. AlpmArchitecturesFn: func() ([]string, error) {
  333. return []string{"x86_64"}, nil
  334. },
  335. IsCorrectVersionInstalledFn: func(s1, s2 string) bool {
  336. return true
  337. },
  338. LocalPackageFn: func(s string) mock.IPackage {
  339. if s == "jellyfin-server" {
  340. return &mock.Package{
  341. PName: "jellyfin-server",
  342. PBase: "jellyfin-server",
  343. PVersion: "10.8.4-1",
  344. PDB: mock.NewDB("community"),
  345. }
  346. }
  347. return nil
  348. },
  349. LocalSatisfierExistsFn: func(s string) bool {
  350. switch s {
  351. case "dotnet-sdk>=6", "dotnet-sdk<7", "dotnet-runtime>=6", "dotnet-runtime<7", "jellyfin-server=10.8.4", "jellyfin-web=10.8.4":
  352. return false
  353. }
  354. return true
  355. },
  356. SyncSatisfierFn: func(s string) mock.IPackage {
  357. switch s {
  358. case "dotnet-runtime>=6", "dotnet-runtime<7":
  359. return &mock.Package{
  360. PName: "dotnet-runtime-6.0",
  361. PBase: "dotnet-runtime-6.0",
  362. PVersion: "6.0.100-1",
  363. PDB: mock.NewDB("community"),
  364. }
  365. case "dotnet-sdk>=6", "dotnet-sdk<7":
  366. return &mock.Package{
  367. PName: "dotnet-sdk-6.0",
  368. PBase: "dotnet-sdk-6.0",
  369. PVersion: "6.0.100-1",
  370. PDB: mock.NewDB("community"),
  371. }
  372. }
  373. return nil
  374. },
  375. InstalledRemotePackageNamesFn: func() []string { return []string{} },
  376. }
  377. run := &runtime.Runtime{
  378. Cfg: &settings.Configuration{
  379. RemoveMake: "no",
  380. },
  381. Logger: newTestLogger(),
  382. CmdBuilder: cmdBuilder,
  383. VCSStore: &vcs.Mock{},
  384. AURClient: &mockaur.MockAUR{
  385. GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
  386. return []aur.Pkg{}, nil
  387. },
  388. },
  389. }
  390. err = handleCmd(context.Background(), run, cmdArgs, db)
  391. require.NoError(t, err)
  392. require.Len(t, mockRunner.ShowCalls, len(wantShow), "show calls: %v", mockRunner.ShowCalls)
  393. require.Len(t, mockRunner.CaptureCalls, len(wantCapture))
  394. for i, call := range mockRunner.ShowCalls {
  395. show := call.Args[0].(*exec.Cmd).String()
  396. show = strings.ReplaceAll(show, tmpDir, "/testdir") // replace the temp dir with a static path
  397. show = strings.ReplaceAll(show, makepkgBin, "makepkg")
  398. show = strings.ReplaceAll(show, pacmanBin, "pacman")
  399. show = strings.ReplaceAll(show, gitBin, "pacman")
  400. // options are in a different order on different systems and on CI root user is used
  401. assert.Subset(t, strings.Split(show, " "), strings.Split(wantShow[i], " "), fmt.Sprintf("%d - %s", i, show))
  402. }
  403. }
  404. func TestIntegrationLocalInstallGenerateSRCINFO(t *testing.T) {
  405. makepkgBin := t.TempDir() + "/makepkg"
  406. pacmanBin := t.TempDir() + "/pacman"
  407. gitBin := t.TempDir() + "/git"
  408. tmpDir := t.TempDir()
  409. f, err := os.OpenFile(makepkgBin, os.O_RDONLY|os.O_CREATE, 0o755)
  410. require.NoError(t, err)
  411. require.NoError(t, f.Close())
  412. f, err = os.OpenFile(pacmanBin, os.O_RDONLY|os.O_CREATE, 0o755)
  413. require.NoError(t, err)
  414. require.NoError(t, f.Close())
  415. f, err = os.OpenFile(gitBin, os.O_RDONLY|os.O_CREATE, 0o755)
  416. require.NoError(t, err)
  417. require.NoError(t, f.Close())
  418. srcinfo, err := os.ReadFile("testdata/jfin/.SRCINFO")
  419. require.NoError(t, err)
  420. assert.True(t, strings.HasPrefix(string(srcinfo), "pkgbase = jellyfin"), string(srcinfo))
  421. targetDir := t.TempDir()
  422. f, err = os.OpenFile(filepath.Join(targetDir, "PKGBUILD"), os.O_RDONLY|os.O_CREATE, 0o755)
  423. require.NoError(t, err)
  424. require.NoError(t, f.Close())
  425. tars := []string{
  426. tmpDir + "/jellyfin-10.8.4-1-x86_64.pkg.tar.zst",
  427. tmpDir + "/jellyfin-web-10.8.4-1-x86_64.pkg.tar.zst",
  428. tmpDir + "/jellyfin-server-10.8.4-1-x86_64.pkg.tar.zst",
  429. }
  430. wantShow := []string{
  431. "makepkg --verifysource --skippgpcheck -Ccf",
  432. "pacman -S --config /etc/pacman.conf -- community/dotnet-sdk-6.0 community/dotnet-runtime-6.0",
  433. "pacman -D -q --asdeps --config /etc/pacman.conf -- dotnet-runtime-6.0 dotnet-sdk-6.0",
  434. "makepkg --nobuild -fC --ignorearch",
  435. "makepkg -c --nobuild --noextract --ignorearch",
  436. "makepkg --nobuild -fC --ignorearch",
  437. "makepkg -c --nobuild --noextract --ignorearch",
  438. "pacman -U --config /etc/pacman.conf -- /testdir/jellyfin-server-10.8.4-1-x86_64.pkg.tar.zst /testdir/jellyfin-web-10.8.4-1-x86_64.pkg.tar.zst",
  439. "pacman -D -q --asexplicit --config /etc/pacman.conf -- jellyfin-server jellyfin-web",
  440. "makepkg --nobuild -fC --ignorearch",
  441. "makepkg -c --nobuild --noextract --ignorearch",
  442. "pacman -U --config /etc/pacman.conf -- /testdir/jellyfin-10.8.4-1-x86_64.pkg.tar.zst",
  443. "pacman -D -q --asexplicit --config /etc/pacman.conf -- jellyfin",
  444. }
  445. wantCapture := []string{
  446. "makepkg --printsrcinfo",
  447. "makepkg --packagelist",
  448. "git -C testdata/jfin git reset --hard HEAD",
  449. "git -C testdata/jfin git merge --no-edit --ff",
  450. "makepkg --packagelist",
  451. "makepkg --packagelist",
  452. }
  453. captureOverride := func(cmd *exec.Cmd) (stdout string, stderr string, err error) {
  454. for _, arg := range cmd.Args {
  455. if arg == "--printsrcinfo" {
  456. return string(srcinfo), "", nil
  457. }
  458. }
  459. return strings.Join(tars, "\n"), "", nil
  460. }
  461. once := sync.Once{}
  462. showOverride := func(cmd *exec.Cmd) error {
  463. once.Do(func() {
  464. for _, tar := range tars {
  465. f, err := os.OpenFile(tar, os.O_RDONLY|os.O_CREATE, 0o666)
  466. require.NoError(t, err)
  467. require.NoError(t, f.Close())
  468. }
  469. })
  470. return nil
  471. }
  472. mockRunner := &exe.MockRunner{CaptureFn: captureOverride, ShowFn: showOverride}
  473. cmdBuilder := &exe.CmdBuilder{
  474. MakepkgBin: makepkgBin,
  475. SudoBin: "su",
  476. PacmanBin: pacmanBin,
  477. PacmanConfigPath: "/etc/pacman.conf",
  478. GitBin: "git",
  479. Runner: mockRunner,
  480. SudoLoopEnabled: false,
  481. }
  482. cmdArgs := parser.MakeArguments()
  483. cmdArgs.AddArg("B")
  484. cmdArgs.AddArg("i")
  485. cmdArgs.AddTarget(targetDir)
  486. settings.NoConfirm = true
  487. defer func() { settings.NoConfirm = false }()
  488. db := &mock.DBExecutor{
  489. AlpmArchitecturesFn: func() ([]string, error) {
  490. return []string{"x86_64"}, nil
  491. },
  492. LocalSatisfierExistsFn: func(s string) bool {
  493. switch s {
  494. case "dotnet-sdk>=6", "dotnet-sdk<7", "dotnet-runtime>=6", "dotnet-runtime<7", "jellyfin-server=10.8.4", "jellyfin-web=10.8.4":
  495. return false
  496. }
  497. return true
  498. },
  499. LocalPackageFn: func(string) mock.IPackage { return nil },
  500. SyncSatisfierFn: func(s string) mock.IPackage {
  501. switch s {
  502. case "dotnet-runtime>=6", "dotnet-runtime<7":
  503. return &mock.Package{
  504. PName: "dotnet-runtime-6.0",
  505. PBase: "dotnet-runtime-6.0",
  506. PVersion: "6.0.100-1",
  507. PDB: mock.NewDB("community"),
  508. }
  509. case "dotnet-sdk>=6", "dotnet-sdk<7":
  510. return &mock.Package{
  511. PName: "dotnet-sdk-6.0",
  512. PBase: "dotnet-sdk-6.0",
  513. PVersion: "6.0.100-1",
  514. PDB: mock.NewDB("community"),
  515. }
  516. }
  517. return nil
  518. },
  519. InstalledRemotePackageNamesFn: func() []string { return []string{} },
  520. }
  521. run := &runtime.Runtime{
  522. Cfg: &settings.Configuration{
  523. RemoveMake: "no",
  524. Debug: false,
  525. },
  526. Logger: newTestLogger(),
  527. CmdBuilder: cmdBuilder,
  528. VCSStore: &vcs.Mock{},
  529. AURClient: &mockaur.MockAUR{
  530. GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
  531. return []aur.Pkg{}, nil
  532. },
  533. },
  534. }
  535. err = handleCmd(context.Background(), run, cmdArgs, db)
  536. require.NoError(t, err)
  537. require.Len(t, mockRunner.ShowCalls, len(wantShow))
  538. require.Len(t, mockRunner.CaptureCalls, len(wantCapture))
  539. for i, call := range mockRunner.ShowCalls {
  540. show := call.Args[0].(*exec.Cmd).String()
  541. show = strings.ReplaceAll(show, tmpDir, "/testdir") // replace the temp dir with a static path
  542. show = strings.ReplaceAll(show, makepkgBin, "makepkg")
  543. show = strings.ReplaceAll(show, pacmanBin, "pacman")
  544. show = strings.ReplaceAll(show, gitBin, "pacman")
  545. // options are in a different order on different systems and on CI root user is used
  546. assert.Subset(t, strings.Split(show, " "), strings.Split(wantShow[i], " "), fmt.Sprintf("%d - %s", i, show))
  547. }
  548. }
  549. func TestIntegrationLocalInstallMissingFiles(t *testing.T) {
  550. makepkgBin := t.TempDir() + "/makepkg"
  551. pacmanBin := t.TempDir() + "/pacman"
  552. gitBin := t.TempDir() + "/git"
  553. tmpDir := t.TempDir()
  554. f, err := os.OpenFile(makepkgBin, os.O_RDONLY|os.O_CREATE, 0o755)
  555. require.NoError(t, err)
  556. require.NoError(t, f.Close())
  557. f, err = os.OpenFile(pacmanBin, os.O_RDONLY|os.O_CREATE, 0o755)
  558. require.NoError(t, err)
  559. require.NoError(t, f.Close())
  560. f, err = os.OpenFile(gitBin, os.O_RDONLY|os.O_CREATE, 0o755)
  561. require.NoError(t, err)
  562. require.NoError(t, f.Close())
  563. srcinfo, err := os.ReadFile("testdata/jfin/.SRCINFO")
  564. require.NoError(t, err)
  565. targetDir := t.TempDir()
  566. tars := []string{
  567. tmpDir + "/jellyfin-10.8.4-1-x86_64.pkg.tar.zst",
  568. tmpDir + "/jellyfin-web-10.8.4-1-x86_64.pkg.tar.zst",
  569. tmpDir + "/jellyfin-server-10.8.4-1-x86_64.pkg.tar.zst",
  570. }
  571. wantShow := []string{}
  572. wantCapture := []string{}
  573. captureOverride := func(cmd *exec.Cmd) (stdout string, stderr string, err error) {
  574. if cmd.Args[1] == "--printsrcinfo" {
  575. return string(srcinfo), "", nil
  576. }
  577. return strings.Join(tars, "\n"), "", nil
  578. }
  579. once := sync.Once{}
  580. showOverride := func(cmd *exec.Cmd) error {
  581. once.Do(func() {
  582. for _, tar := range tars {
  583. f, err := os.OpenFile(tar, os.O_RDONLY|os.O_CREATE, 0o666)
  584. require.NoError(t, err)
  585. require.NoError(t, f.Close())
  586. }
  587. })
  588. return nil
  589. }
  590. mockRunner := &exe.MockRunner{CaptureFn: captureOverride, ShowFn: showOverride}
  591. cmdBuilder := &exe.CmdBuilder{
  592. MakepkgBin: makepkgBin,
  593. SudoBin: "su",
  594. PacmanBin: pacmanBin,
  595. PacmanConfigPath: "/etc/pacman.conf",
  596. GitBin: "git",
  597. Runner: mockRunner,
  598. SudoLoopEnabled: false,
  599. }
  600. cmdArgs := parser.MakeArguments()
  601. cmdArgs.AddArg("B")
  602. cmdArgs.AddArg("i")
  603. cmdArgs.AddTarget(targetDir)
  604. settings.NoConfirm = true
  605. defer func() { settings.NoConfirm = false }()
  606. db := &mock.DBExecutor{
  607. AlpmArchitecturesFn: func() ([]string, error) {
  608. return []string{"x86_64"}, nil
  609. },
  610. LocalSatisfierExistsFn: func(s string) bool {
  611. switch s {
  612. case "dotnet-sdk>=6", "dotnet-sdk<7", "dotnet-runtime>=6", "dotnet-runtime<7", "jellyfin-server=10.8.4", "jellyfin-web=10.8.4":
  613. return false
  614. }
  615. return true
  616. },
  617. SyncSatisfierFn: func(s string) mock.IPackage {
  618. switch s {
  619. case "dotnet-runtime>=6", "dotnet-runtime<7":
  620. return &mock.Package{
  621. PName: "dotnet-runtime-6.0",
  622. PBase: "dotnet-runtime-6.0",
  623. PVersion: "6.0.100-1",
  624. PDB: mock.NewDB("community"),
  625. }
  626. case "dotnet-sdk>=6", "dotnet-sdk<7":
  627. return &mock.Package{
  628. PName: "dotnet-sdk-6.0",
  629. PBase: "dotnet-sdk-6.0",
  630. PVersion: "6.0.100-1",
  631. PDB: mock.NewDB("community"),
  632. }
  633. }
  634. return nil
  635. },
  636. }
  637. config := &runtime.Runtime{
  638. Cfg: &settings.Configuration{
  639. RemoveMake: "no",
  640. Debug: false,
  641. },
  642. Logger: newTestLogger(),
  643. CmdBuilder: cmdBuilder,
  644. VCSStore: &vcs.Mock{},
  645. AURClient: &mockaur.MockAUR{
  646. GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
  647. return []aur.Pkg{}, nil
  648. },
  649. },
  650. }
  651. err = handleCmd(context.Background(), config, cmdArgs, db)
  652. require.ErrorIs(t, err, dep.ErrNoBuildFiles)
  653. require.Len(t, mockRunner.ShowCalls, len(wantShow))
  654. require.Len(t, mockRunner.CaptureCalls, len(wantCapture))
  655. for i, call := range mockRunner.ShowCalls {
  656. show := call.Args[0].(*exec.Cmd).String()
  657. show = strings.ReplaceAll(show, tmpDir, "/testdir") // replace the temp dir with a static path
  658. show = strings.ReplaceAll(show, makepkgBin, "makepkg")
  659. show = strings.ReplaceAll(show, pacmanBin, "pacman")
  660. show = strings.ReplaceAll(show, gitBin, "pacman")
  661. // options are in a different order on different systems and on CI root user is used
  662. assert.Subset(t, strings.Split(show, " "), strings.Split(wantShow[i], " "), fmt.Sprintf("%d - %s", i, show))
  663. }
  664. }
  665. func TestIntegrationLocalInstallWithDepsProvides(t *testing.T) {
  666. makepkgBin := t.TempDir() + "/makepkg"
  667. pacmanBin := t.TempDir() + "/pacman"
  668. gitBin := t.TempDir() + "/git"
  669. tmpDir := t.TempDir()
  670. f, err := os.OpenFile(makepkgBin, os.O_RDONLY|os.O_CREATE, 0o755)
  671. require.NoError(t, err)
  672. require.NoError(t, f.Close())
  673. f, err = os.OpenFile(pacmanBin, os.O_RDONLY|os.O_CREATE, 0o755)
  674. require.NoError(t, err)
  675. require.NoError(t, f.Close())
  676. f, err = os.OpenFile(gitBin, os.O_RDONLY|os.O_CREATE, 0o755)
  677. require.NoError(t, err)
  678. require.NoError(t, f.Close())
  679. tars := []string{
  680. tmpDir + "/ceph-bin-17.2.6-2-x86_64.pkg.tar.zst",
  681. tmpDir + "/ceph-libs-bin-17.2.6-2-x86_64.pkg.tar.zst",
  682. }
  683. wantShow := []string{
  684. "makepkg --verifysource --skippgpcheck -Ccf",
  685. "makepkg --nobuild -fC --ignorearch",
  686. "makepkg -c --nobuild --noextract --ignorearch",
  687. "pacman -U --config /etc/pacman.conf -- /testdir/ceph-libs-bin-17.2.6-2-x86_64.pkg.tar.zst",
  688. "pacman -D -q --asexplicit --config /etc/pacman.conf -- ceph-libs-bin",
  689. "makepkg --nobuild -fC --ignorearch",
  690. "makepkg -c --nobuild --noextract --ignorearch",
  691. "pacman -U --config /etc/pacman.conf -- /testdir/ceph-bin-17.2.6-2-x86_64.pkg.tar.zst",
  692. "pacman -D -q --asexplicit --config /etc/pacman.conf -- ceph-bin",
  693. }
  694. wantCapture := []string{
  695. "git -C testdata/cephbin git reset --hard HEAD",
  696. "git -C testdata/cephbin git merge --no-edit --ff",
  697. "makepkg --packagelist",
  698. "makepkg --packagelist",
  699. }
  700. captureOverride := func(cmd *exec.Cmd) (stdout string, stderr string, err error) {
  701. return strings.Join(tars, "\n"), "", nil
  702. }
  703. once := sync.Once{}
  704. showOverride := func(cmd *exec.Cmd) error {
  705. once.Do(func() {
  706. for _, tar := range tars {
  707. f, err := os.OpenFile(tar, os.O_RDONLY|os.O_CREATE, 0o666)
  708. require.NoError(t, err)
  709. require.NoError(t, f.Close())
  710. }
  711. })
  712. return nil
  713. }
  714. mockRunner := &exe.MockRunner{CaptureFn: captureOverride, ShowFn: showOverride}
  715. cmdBuilder := &exe.CmdBuilder{
  716. MakepkgBin: makepkgBin,
  717. SudoBin: "su",
  718. PacmanBin: pacmanBin,
  719. PacmanConfigPath: "/etc/pacman.conf",
  720. GitBin: "git",
  721. Runner: mockRunner,
  722. SudoLoopEnabled: false,
  723. }
  724. cmdArgs := parser.MakeArguments()
  725. cmdArgs.AddArg("B")
  726. cmdArgs.AddArg("i")
  727. cmdArgs.AddTarget("testdata/cephbin")
  728. settings.NoConfirm = true
  729. defer func() { settings.NoConfirm = false }()
  730. db := &mock.DBExecutor{
  731. AlpmArchitecturesFn: func() ([]string, error) {
  732. return []string{"x86_64"}, nil
  733. },
  734. LocalSatisfierExistsFn: func(s string) bool {
  735. switch s {
  736. case "ceph=17.2.6-2", "ceph-libs=17.2.6-2":
  737. return false
  738. }
  739. return true
  740. },
  741. SyncSatisfierFn: func(s string) mock.IPackage {
  742. return nil
  743. },
  744. LocalPackageFn: func(s string) mock.IPackage { return nil },
  745. InstalledRemotePackageNamesFn: func() []string { return []string{} },
  746. }
  747. config := &runtime.Runtime{
  748. Cfg: &settings.Configuration{
  749. RemoveMake: "no",
  750. },
  751. Logger: newTestLogger(),
  752. CmdBuilder: cmdBuilder,
  753. VCSStore: &vcs.Mock{},
  754. AURClient: &mockaur.MockAUR{
  755. GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
  756. return []aur.Pkg{}, nil
  757. },
  758. },
  759. }
  760. err = handleCmd(context.Background(), config, cmdArgs, db)
  761. require.NoError(t, err)
  762. require.Len(t, mockRunner.ShowCalls, len(wantShow))
  763. require.Len(t, mockRunner.CaptureCalls, len(wantCapture))
  764. for i, call := range mockRunner.ShowCalls {
  765. show := call.Args[0].(*exec.Cmd).String()
  766. show = strings.ReplaceAll(show, tmpDir, "/testdir") // replace the temp dir with a static path
  767. show = strings.ReplaceAll(show, makepkgBin, "makepkg")
  768. show = strings.ReplaceAll(show, pacmanBin, "pacman")
  769. show = strings.ReplaceAll(show, gitBin, "pacman")
  770. // options are in a different order on different systems and on CI root user is used
  771. assert.Subset(t, strings.Split(show, " "), strings.Split(wantShow[i], " "), fmt.Sprintf("%d - %s", i, show))
  772. }
  773. }
  774. func TestIntegrationLocalInstallTwoSrcInfosWithDeps(t *testing.T) {
  775. makepkgBin := t.TempDir() + "/makepkg"
  776. pacmanBin := t.TempDir() + "/pacman"
  777. gitBin := t.TempDir() + "/git"
  778. tmpDir1 := t.TempDir()
  779. tmpDir2 := t.TempDir()
  780. f, err := os.OpenFile(makepkgBin, os.O_RDONLY|os.O_CREATE, 0o755)
  781. require.NoError(t, err)
  782. require.NoError(t, f.Close())
  783. f, err = os.OpenFile(pacmanBin, os.O_RDONLY|os.O_CREATE, 0o755)
  784. require.NoError(t, err)
  785. require.NoError(t, f.Close())
  786. f, err = os.OpenFile(gitBin, os.O_RDONLY|os.O_CREATE, 0o755)
  787. require.NoError(t, err)
  788. require.NoError(t, f.Close())
  789. pkgsTars := []string{
  790. tmpDir1 + "/libzip-git-1.9.2.r166.gd2c47d0f-1-x86_64.pkg.tar.zst",
  791. tmpDir2 + "/gourou-0.8.1-4-x86_64.pkg.tar.zst",
  792. }
  793. wantShow := []string{
  794. "makepkg --verifysource --skippgpcheck -Ccf",
  795. "makepkg --verifysource --skippgpcheck -Ccf",
  796. "makepkg --nobuild -fC --ignorearch",
  797. "makepkg -c --nobuild --noextract --ignorearch",
  798. "pacman -U --config /etc/pacman.conf -- /testdir1/libzip-git-1.9.2.r166.gd2c47d0f-1-x86_64.pkg.tar.zst",
  799. "pacman -D -q --asexplicit --config /etc/pacman.conf -- libzip-git",
  800. "makepkg --nobuild -fC --ignorearch",
  801. "makepkg -c --nobuild --noextract --ignorearch",
  802. "pacman -U --config /etc/pacman.conf -- /testdir2/gourou-0.8.1-4-x86_64.pkg.tar.zst",
  803. "pacman -D -q --asexplicit --config /etc/pacman.conf -- gourou",
  804. }
  805. wantCapture := []string{
  806. "git -C testdata/gourou git reset --hard HEAD",
  807. "git -C testdata/gourou git merge --no-edit --ff",
  808. "git -C testdata/libzip-git git reset --hard HEAD",
  809. "git -C testdata/libzip-git git merge --no-edit --ff",
  810. "makepkg --packagelist",
  811. "makepkg --packagelist",
  812. }
  813. captureCounter := 0
  814. captureOverride := func(cmd *exec.Cmd) (stdout string, stderr string, err error) {
  815. captureCounter++
  816. switch captureCounter {
  817. case 5:
  818. return pkgsTars[0] + "\n", "", nil
  819. case 6:
  820. return pkgsTars[1] + "\n", "", nil
  821. default:
  822. return "", "", nil
  823. }
  824. }
  825. once := sync.Once{}
  826. showOverride := func(cmd *exec.Cmd) error {
  827. once.Do(func() {
  828. for _, tar := range pkgsTars {
  829. f, err := os.OpenFile(tar, os.O_RDONLY|os.O_CREATE, 0o666)
  830. require.NoError(t, err)
  831. require.NoError(t, f.Close())
  832. }
  833. })
  834. return nil
  835. }
  836. mockRunner := &exe.MockRunner{CaptureFn: captureOverride, ShowFn: showOverride}
  837. cmdBuilder := &exe.CmdBuilder{
  838. MakepkgBin: makepkgBin,
  839. SudoBin: "su",
  840. PacmanBin: pacmanBin,
  841. PacmanConfigPath: "/etc/pacman.conf",
  842. GitBin: "git",
  843. Runner: mockRunner,
  844. SudoLoopEnabled: false,
  845. }
  846. cmdArgs := parser.MakeArguments()
  847. cmdArgs.AddArg("B")
  848. cmdArgs.AddArg("i")
  849. cmdArgs.AddTarget("testdata/gourou")
  850. cmdArgs.AddTarget("testdata/libzip-git")
  851. settings.NoConfirm = true
  852. defer func() { settings.NoConfirm = false }()
  853. db := &mock.DBExecutor{
  854. AlpmArchitecturesFn: func() ([]string, error) {
  855. return []string{"x86_64"}, nil
  856. },
  857. LocalSatisfierExistsFn: func(s string) bool {
  858. switch s {
  859. case "gourou", "libzip", "libzip-git":
  860. return false
  861. }
  862. return true
  863. },
  864. SyncSatisfierFn: func(s string) mock.IPackage {
  865. return nil
  866. },
  867. LocalPackageFn: func(s string) mock.IPackage { return nil },
  868. InstalledRemotePackageNamesFn: func() []string { return []string{} },
  869. }
  870. run := &runtime.Runtime{
  871. Cfg: &settings.Configuration{
  872. RemoveMake: "no",
  873. },
  874. Logger: newTestLogger(),
  875. CmdBuilder: cmdBuilder,
  876. VCSStore: &vcs.Mock{},
  877. AURClient: &mockaur.MockAUR{
  878. GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
  879. return []aur.Pkg{}, nil
  880. },
  881. },
  882. }
  883. err = handleCmd(context.Background(), run, cmdArgs, db)
  884. require.NoError(t, err)
  885. require.Len(t, mockRunner.ShowCalls, len(wantShow))
  886. require.Len(t, mockRunner.CaptureCalls, len(wantCapture))
  887. for i, call := range mockRunner.ShowCalls {
  888. show := call.Args[0].(*exec.Cmd).String()
  889. show = strings.ReplaceAll(show, tmpDir1, "/testdir1") // replace the temp dir with a static path
  890. show = strings.ReplaceAll(show, tmpDir2, "/testdir2") // replace the temp dir with a static path
  891. show = strings.ReplaceAll(show, makepkgBin, "makepkg")
  892. show = strings.ReplaceAll(show, pacmanBin, "pacman")
  893. show = strings.ReplaceAll(show, gitBin, "pacman")
  894. // options are in a different order on different systems and on CI root user is used
  895. assert.Subset(t, strings.Split(show, " "), strings.Split(wantShow[i], " "), fmt.Sprintf("%d - %s", i, show))
  896. }
  897. }