local_install_test.go 30 KB

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