local_install_test.go 31 KB

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