// 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 . 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 { t.Run(tc.name, func(t *testing.T) { s, res := prepareSync(tc.req, &testRunner{ t: t, 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("err = %#v, want %#v", err, tc.expErr) } dbg := res.Wait() if !reflect.DeepEqual(tc.expResp, s.resp) { t.Errorf("resp: %s", cmp.Diff(tc.expResp, s.resp)) } if !reflect.DeepEqual(tc.expDbg, dbg) { t.Errorf("dbg: %s", cmp.Diff(tc.expDbg, dbg)) } }) } }