--- /dev/null
+// Copyright (C) 2021 Simon Ruderich
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+package sync
+
+import (
+ "bytes"
+ "fmt"
+ "os"
+ "os/exec"
+ "reflect"
+ "testing"
+
+ "github.com/google/go-cmp/cmp"
+
+ "ruderich.org/simon/safcm"
+)
+
+func TestSyncPackagesDebian(t *testing.T) {
+ tests := []struct {
+ name string
+ req safcm.MsgSyncReq
+ stdout [][]byte
+ stderr [][]byte
+ errors []error
+ expCmds []*exec.Cmd
+ expDbg []string
+ expResp safcm.MsgSyncResp
+ expErr error
+ }{
+
+ // NOTE: Also update MsgSyncResp in safcm test cases when
+ // changing anything here!
+
+ {
+ "packages already installed",
+ safcm.MsgSyncReq{
+ Packages: []string{
+ "package-one",
+ "package-two",
+ },
+ },
+ [][]byte{
+ []byte(`install ok installed golang
+install ok installed golang-1.16
+install ok installed golang-1.16-doc
+install ok installed golang-1.16-go
+install ok installed golang-1.16-src
+hold ok installed package-one
+install ok installed package-two
+`),
+ },
+ [][]byte{nil},
+ []error{nil},
+ []*exec.Cmd{&exec.Cmd{
+ Path: "/usr/bin/dpkg-query",
+ Args: []string{
+ "/usr/bin/dpkg-query",
+ "--show",
+ `--showformat=${Status}\t${Package}\n`,
+ },
+ Stdout: &bytes.Buffer{},
+ Stderr: &bytes.Buffer{},
+ }},
+ []string{
+ "4: sync remote: packages: detected debian",
+ `4: sync remote: packages: running "/usr/bin/dpkg-query" "--show" "--showformat=${Status}\\t${Package}\\n"`,
+ `5: sync remote: packages: command stdout:
+install ok installed golang
+install ok installed golang-1.16
+install ok installed golang-1.16-doc
+install ok installed golang-1.16-go
+install ok installed golang-1.16-src
+hold ok installed package-one
+install ok installed package-two
+`,
+ "4: sync remote: packages: checking package-one package-two",
+ },
+ safcm.MsgSyncResp{},
+ nil,
+ },
+
+ {
+ "packages not yet installed",
+ safcm.MsgSyncReq{
+ Packages: []string{
+ "package-one",
+ "package-two",
+ "package-three",
+ },
+ },
+ [][]byte{
+ []byte(`install ok installed golang
+install ok installed golang-1.16
+install ok installed golang-1.16-doc
+install ok installed golang-1.16-go
+install ok installed golang-1.16-src
+install ok installed package-two
+`),
+ []byte("fake stdout/stderr"),
+ },
+ [][]byte{nil, nil},
+ []error{nil, nil},
+ []*exec.Cmd{&exec.Cmd{
+ Path: "/usr/bin/dpkg-query",
+ Args: []string{
+ "/usr/bin/dpkg-query",
+ "--show",
+ `--showformat=${Status}\t${Package}\n`,
+ },
+ Stdout: &bytes.Buffer{},
+ Stderr: &bytes.Buffer{},
+ }, &exec.Cmd{
+ Path: "/usr/bin/apt-get",
+ Args: []string{
+ "/usr/bin/apt-get",
+ "install",
+ "--assume-yes",
+ "--no-upgrade",
+ "--no-install-recommends",
+ "-o", "Dpkg::Options::=--force-confdef",
+ "-o", "Dpkg::Options::=--force-confold",
+ "package-one",
+ "package-three",
+ },
+ Env: append(os.Environ(),
+ "DEBIAN_FRONTEND=noninteractive",
+ ),
+ }},
+ []string{
+ "4: sync remote: packages: detected debian",
+ `4: sync remote: packages: running "/usr/bin/dpkg-query" "--show" "--showformat=${Status}\\t${Package}\\n"`,
+ `5: sync remote: packages: command stdout:
+install ok installed golang
+install ok installed golang-1.16
+install ok installed golang-1.16-doc
+install ok installed golang-1.16-go
+install ok installed golang-1.16-src
+install ok installed package-two
+`,
+ "4: sync remote: packages: checking package-one package-two package-three",
+ "3: sync remote: packages: installing package-one package-three",
+ `4: sync remote: packages: running "/usr/bin/apt-get" "install" "--assume-yes" "--no-upgrade" "--no-install-recommends" "-o" "Dpkg::Options::=--force-confdef" "-o" "Dpkg::Options::=--force-confold" "package-one" "package-three"`,
+ "5: sync remote: packages: command output:\nfake stdout/stderr",
+ },
+ safcm.MsgSyncResp{
+ PackageChanges: []safcm.PackageChange{
+ {
+ Name: "package-one",
+ },
+ {
+ Name: "package-three",
+ },
+ },
+ },
+ nil,
+ },
+
+ {
+ "packages not yet installed (error)",
+ safcm.MsgSyncReq{
+ Packages: []string{
+ "package-one",
+ "package-two",
+ },
+ },
+ [][]byte{
+ []byte(`install ok installed golang
+install ok installed golang-1.16
+install ok installed golang-1.16-doc
+install ok installed golang-1.16-go
+install ok installed golang-1.16-src
+`),
+ []byte("fake stdout/stderr"),
+ },
+ [][]byte{nil, nil},
+ []error{
+ nil,
+ fmt.Errorf("fake error"),
+ },
+ []*exec.Cmd{&exec.Cmd{
+ Path: "/usr/bin/dpkg-query",
+ Args: []string{
+ "/usr/bin/dpkg-query",
+ "--show",
+ `--showformat=${Status}\t${Package}\n`,
+ },
+ Stdout: &bytes.Buffer{},
+ Stderr: &bytes.Buffer{},
+ }, &exec.Cmd{
+ Path: "/usr/bin/apt-get",
+ Args: []string{
+ "/usr/bin/apt-get",
+ "install",
+ "--assume-yes",
+ "--no-upgrade",
+ "--no-install-recommends",
+ "-o", "Dpkg::Options::=--force-confdef",
+ "-o", "Dpkg::Options::=--force-confold",
+ "package-one",
+ "package-two",
+ },
+ Env: append(os.Environ(),
+ "DEBIAN_FRONTEND=noninteractive",
+ ),
+ }},
+ []string{
+ "4: sync remote: packages: detected debian",
+ `4: sync remote: packages: running "/usr/bin/dpkg-query" "--show" "--showformat=${Status}\\t${Package}\\n"`,
+ `5: sync remote: packages: command stdout:
+install ok installed golang
+install ok installed golang-1.16
+install ok installed golang-1.16-doc
+install ok installed golang-1.16-go
+install ok installed golang-1.16-src
+`,
+ "4: sync remote: packages: checking package-one package-two",
+ "3: sync remote: packages: installing package-one package-two",
+ `4: sync remote: packages: running "/usr/bin/apt-get" "install" "--assume-yes" "--no-upgrade" "--no-install-recommends" "-o" "Dpkg::Options::=--force-confdef" "-o" "Dpkg::Options::=--force-confold" "package-one" "package-two"`,
+ "5: sync remote: packages: command output:\nfake stdout/stderr",
+ },
+ safcm.MsgSyncResp{
+ PackageChanges: []safcm.PackageChange{
+ {
+ Name: "package-one",
+ },
+ {
+ Name: "package-two",
+ },
+ },
+ },
+ fmt.Errorf(`"/usr/bin/apt-get" "install" "--assume-yes" "--no-upgrade" "--no-install-recommends" "-o" "Dpkg::Options::=--force-confdef" "-o" "Dpkg::Options::=--force-confold" "package-one" "package-two" failed: fake error; output: "fake stdout/stderr"`),
+ },
+
+ {
+ "packages not yet installed (dry-run)",
+ safcm.MsgSyncReq{
+ DryRun: true,
+ Packages: []string{
+ "package-one",
+ "package-two",
+ },
+ },
+ [][]byte{
+ []byte(`install ok installed golang
+install ok installed golang-1.16
+install ok installed golang-1.16-doc
+install ok installed golang-1.16-go
+install ok installed golang-1.16-src
+`),
+ },
+ [][]byte{nil},
+ []error{nil},
+ []*exec.Cmd{&exec.Cmd{
+ Path: "/usr/bin/dpkg-query",
+ Args: []string{
+ "/usr/bin/dpkg-query",
+ "--show",
+ `--showformat=${Status}\t${Package}\n`,
+ },
+ Stdout: &bytes.Buffer{},
+ Stderr: &bytes.Buffer{},
+ }},
+ []string{
+ "4: sync remote: packages: detected debian",
+ `4: sync remote: packages: running "/usr/bin/dpkg-query" "--show" "--showformat=${Status}\\t${Package}\\n"`,
+ `5: sync remote: packages: command stdout:
+install ok installed golang
+install ok installed golang-1.16
+install ok installed golang-1.16-doc
+install ok installed golang-1.16-go
+install ok installed golang-1.16-src
+`,
+ "4: sync remote: packages: checking package-one package-two",
+ },
+ safcm.MsgSyncResp{
+ PackageChanges: []safcm.PackageChange{
+ {
+ Name: "package-one",
+ },
+ {
+ Name: "package-two",
+ },
+ },
+ },
+ nil,
+ },
+ }
+
+ for _, tc := range tests {
+ s, res := prepareSync(tc.req, &testRunner{
+ t: t,
+ name: tc.name,
+ expCmds: tc.expCmds,
+ resStdout: tc.stdout,
+ resStderr: tc.stderr,
+ resError: tc.errors,
+ })
+
+ err := s.syncPackagesDebian()
+ // Ugly but the simplest way to compare errors (including nil)
+ if fmt.Sprintf("%s", err) != fmt.Sprintf("%s", tc.expErr) {
+ t.Errorf("%s: err = %#v, want %#v",
+ tc.name, err, tc.expErr)
+ }
+ dbg := res.Wait()
+
+ if !reflect.DeepEqual(tc.expResp, s.resp) {
+ t.Errorf("%s: resp: %s", tc.name,
+ cmp.Diff(tc.expResp, s.resp))
+ }
+ if !reflect.DeepEqual(tc.expDbg, dbg) {
+ t.Errorf("%s: dbg: %s", tc.name,
+ cmp.Diff(tc.expDbg, dbg))
+ }
+ }
+}