local_install_test.go 31 KB

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