// Copyright 2024 Matthew Rich . All rights reserved package resource import ( "context" _ "encoding/json" "fmt" "github.com/stretchr/testify/assert" _ "gopkg.in/yaml.v3" _ "io" "log/slog" _ "net/http" _ "net/http/httptest" _ "net/url" _ "os" _ "strings" "testing" "decl/internal/command" ) func TestNewPackageResource(t *testing.T) { p := NewPackage() assert.NotNil(t, p) } func TestPackageApplyResourceTransformation(t *testing.T) { p := NewPackage() assert.NotNil(t, p) //e := f.Apply() //assert.Equal(t, nil, e) } func TestCommandExtractor(t *testing.T) { packages := map[PackageType][]struct{ Name string; Version string; Input string } { PackageTypeApk: []struct{ Name string; Version string; Input string }{ /* { Name: "alpine-baselayout", Version: "3.6.5-r0", Input: `alpine-baselayout-3.6.5-r0 x86_64 {alpine-baselayout} (GPL-2.0-only) [installed]` }, { Name: "alpine-baselayout-data", Version: "3.6.5-r0", Input: `alpine-baselayout-data-3.6.5-r0 x86_64 {alpine-baselayout} (GPL-2.0-only) [installed]` }, { Name: "alpine-keys", Version: "2.4-r1", Input: `alpine-keys-2.4-r1 x86_64 {alpine-keys} (MIT) [installed]` }, { Name: "apk-tools", Version: "2.14.4-r0", Input: `apk-tools-2.14.4-r0 x86_64 {apk-tools} (GPL-2.0-only) [installed]` }, { Name: "busybox", Version: "1.36.1-r29", Input: `busybox-1.36.1-r29 x86_64 {busybox} (GPL-2.0-only) [installed]` }, { Name: "busybox-binsh", Version: "1.36.1-r20", Input: `busybox-binsh-1.36.1-r29 x86_64 {busybox} (GPL-2.0-only) [installed]` }, { Name: "ca-certificates", Version: "20240705-r0", Input: `ca-certificates-20240705-r0 x86_64 {ca-certificates} (MPL-2.0 AND MIT) [installed]` }, { Name: "ca-certificates-bundle", Version: "20240705-r0", Input: `ca-certificates-bundle-20240705-r0 x86_64 {ca-certificates} (MPL-2.0 AND MIT) [installed]` }, { Name: "libcrypto3", Version: "3.3.1-r3", Input: `libcrypto3-3.3.1-r3 x86_64 {openssl} (Apache-2.0) [installed]` }, { Name: "libssl3", Version: "3.3.1-r3", Input: `libssl3-3.3.1-r3 x86_64 {openssl} (Apache-2.0) [installed]` }, { Name: "musl", Version: "1.2.5-r0", Input: `musl-1.2.5-r0 x86_64 {musl} (MIT) [installed]` }, { Name: "musl-utils", Version: "1.2.5-r0", Input: `musl-utils-1.2.5-r0 x86_64 {musl} (MIT AND BSD-2-Clause AND GPL-2.0-or-later) [installed]` }, { Name: "scanelf", Version: "1.3.7-r2", Input: `scanelf-1.3.7-r2 x86_64 {pax-utils} (GPL-2.0-only) [installed]` }, { Name: "ssl_client", Version: "1.36.1-r29", Input: `ssl_client-1.36.1-r29 x86_64 {busybox} (GPL-2.0-only) [installed]` }, { Name: "zlib", Version: "1.3.1-r1", Input: `zlib-1.3.1-r1 x86_64 {zlib} (Zlib) [installed]` }, */ { Name: "alpine-baselayout", Version: "3.6.5", Input: `alpine-baselayout-3.6.5-r0` }, { Name: "alpine-baselayout-data", Version: "3.6.5", Input: `alpine-baselayout-data-3.6.5-r0` }, { Name: "alpine-keys", Version: "2.4", Input: `alpine-keys-2.4-r1` }, { Name: "apk-tools", Version: "2.14.4", Input: `apk-tools-2.14.4-r0` }, { Name: "busybox", Version: "1.36.1", Input: `busybox-1.36.1-r29` }, { Name: "busybox-binsh", Version: "1.36.1", Input: `busybox-binsh-1.36.1-r29` }, { Name: "ca-certificates", Version: "20240705", Input: `ca-certificates-20240705-r0` }, { Name: "ca-certificates-bundle", Version: "20240705", Input: `ca-certificates-bundle-20240705-r0` }, { Name: "libcrypto3", Version: "3.3.1", Input: `libcrypto3-3.3.1-r3` }, { Name: "libssl3", Version: "3.3.1", Input: `libssl3-3.3.1-r3` }, { Name: "musl", Version: "1.2.5", Input: `musl-1.2.5-r0` }, { Name: "musl-utils", Version: "1.2.5", Input: `musl-utils-1.2.5-r0` }, { Name: "scanelf", Version: "1.3.7", Input: `scanelf-1.3.7-r2` }, { Name: "ssl_client", Version: "1.36.1", Input: `ssl_client-1.36.1-r29` }, { Name: "zlib", Version: "1.3.1", Input: `zlib-1.3.1-r1` }, }, PackageTypeApt: []struct{ Name string; Version string; Input string }{ { Name: "bluez-meshd", Version: "5.64-0ubuntu1pop1~1674313994~22.04~579884f~dev", Input: ` Package: bluez-meshd Status: install ok installed Priority: optional Section: admin Installed-Size: 937 Maintainer: Ubuntu Bluetooth team Architecture: amd64 Source: bluez Version: 5.64-0ubuntu1pop1~1674313994~22.04~579884f~dev Depends: libc6 (>= 2.34), libdbus-1-3 (>= 1.9.14), libglib2.0-0 (>= 2.28.0), libjson-c5 (>= 0.15), libreadline8 (>= 6.0) Conffiles: /etc/dbus-1/system.d/bluetooth-mesh.conf 95c2a66615065ad1195d5b647555c393 Description: bluetooth mesh daemon The Bluetooth Mesh network is a new Bluetooth feature that extends "Bluetooth Low Energy (BLE)". . This package provides daemon (meshd) and tools that provide Bluetooth mesh functionality. . BlueZ is the official Linux Bluetooth protocol stack. It is an Open Source project distributed under GNU General Public License (GPL). Homepage: http://www.bluez.org Original-Maintainer: Debian Bluetooth Maintainers ` }, { Name: "dconf-gsettings-backend", Version: "0.40.0-3", Input: ` Package: dconf-gsettings-backend Status: install ok installed Priority: optional Section: libs Installed-Size: 83 Maintainer: Ubuntu Developers Architecture: amd64 Multi-Arch: same Source: dconf Version: 0.40.0-3 Provides: gsettings-backend Depends: dconf-service (<< 0.40.0-3.1~), dconf-service (>= 0.40.0-3), libdconf1 (= 0.40.0-3), libc6 (>= 2.14), libglib2.0-0 (>= 2.55.2) Description: simple configuration storage system - GSettings back-end DConf is a low-level key/value database designed for storing desktop environment settings. . This package contains a back-end for GSettings. It is needed by applications accessing settings through GSettings to set custom values and listen for changes. Original-Maintainer: Debian GNOME Maintainers Homepage: https://wiki.gnome.org/Projects/dconf ` }, { Name: "cheese-common", Version: "41.1-1build1", Input: ` Package: cheese-common Status: install ok installed Priority: optional Section: gnome Installed-Size: 912 Maintainer: Ubuntu Developers Architecture: all Multi-Arch: foreign Source: cheese Version: 41.1-1build1 Depends: dconf-gsettings-backend | gsettings-backend Description: Common files for the Cheese tool to take pictures and videos A webcam application that supports image and video capture. Makes it easy to take photos and videos of you, your friends, pets or whatever you want. Allows you to apply fancy visual effects, fine-control image settings and has features such as Multi-Burst mode, Countdown timer for photos. . This package contains the common files and translations. Original-Maintainer: Debian GNOME Maintainers Homepage: https://wiki.gnome.org/Apps/Cheese ` }, { Name: "fonts-lohit-deva", Version: "2.95.4-4", Input: ` Package: fonts-lohit-deva Status: install ok installed Priority: optional Section: fonts Installed-Size: 193 Maintainer: Ubuntu Developers Architecture: all Multi-Arch: foreign Version: 2.95.4-4 Replaces: ttf-devanagari-fonts (<= 1:0.5.12) Breaks: ttf-devanagari-fonts (<= 1:0.5.12) Conffiles: /etc/fonts/conf.avail/59-lohit-devanagari.conf 26750c27821421cef15176c5d6eb490e /etc/fonts/conf.avail/66-lohit-devanagari.conf dfac9388b18ece1d53fed86785070f55 Description: Lohit TrueType font for Devanagari script This package provides Lohit TrueType font for Devanagari script which is used for writing Hindi, Kashmiri, Konkani, Marathi, Maithili, Nepali, Sanskrit, and Sindhi languages. Original-Maintainer: Debian Fonts Task Force Homepage: https://pagure.io/lohit ` }, { Name: "zstd", Version: "1.4.8+dfsg-3build1", Input: ` Package: zstd Status: install ok installed Priority: optional Section: utils Installed-Size: 1655 Maintainer: Ubuntu Developers Architecture: amd64 Source: libzstd Version: 1.4.8+dfsg-3build1 Depends: libc6 (>= 2.34), libgcc-s1 (>= 3.3.1), liblz4-1 (>= 0.0~r127), liblzma5 (>= 5.1.1alpha+20120614), libstdc++6 (>= 12), zlib1g (>= 1:1.1.4) Description: fast lossless compression algorithm -- CLI tool Zstd, short for Zstandard, is a fast lossless compression algorithm, targeting real-time compression scenarios at zlib-level compression ratio. . This package contains the CLI program implementing zstd. Homepage: https://github.com/facebook/zstd Original-Maintainer: Debian Med Packaging Team ` }, }, PackageTypeDnf: []struct{ Name string; Version string; Input string }{ { Name: "man-pages-zh-CN", Version: "1.6.3.6-9.fc40", Input: ` man-pages-zh-CN.noarch 1.6.3.6-9.fc40 @fedora` }, { Name: "memstrack", Version: "0.2.5-4.fc40", Input: ` memstrack.x86_64 0.2.5-4.fc40 @fedora` }, { Name: "mercurial", Version: "6.7.4-3.fc40", Input: ` mercurial.x86_64 6.7.4-3.fc40 @updates` }, { Name: "mesa-dri-drivers", Version: "24.1.7-1.fc40", Input: ` mesa-dri-drivers.x86_64 24.1.7-1.fc40 @updates` }, { Name: "mesa-filesystem", Version: "24.1.7-1.fc40", Input: ` mesa-filesystem.x86_64 24.1.7-1.fc40 @updates` }, { Name: "mesa-libEGL", Version: "24.1.7-1.fc40", Input: ` mesa-libEGL.x86_64 24.1.7-1.fc40 @updates` }, { Name: "mesa-libGL", Version: "24.1.7-1.fc40", Input: ` mesa-libGL.x86_64 24.1.7-1.fc40 @updates` }, { Name: "mesa-libgbm", Version: "24.1.7-1.fc40", Input: ` mesa-libgbm.x86_64 24.1.7-1.fc40 @updates` }, { Name: "mesa-libglapi", Version: "24.1.7-1.fc40", Input: ` mesa-libglapi.x86_64 24.1.7-1.fc40 @updates` }, { Name: "mesa-va-drivers", Version: "24.1.7-1.fc40", Input: ` mesa-va-drivers.x86_64 24.1.7-1.fc40 @updates` }, { Name: "mkfontscale", Version: "1.2.2-6.fc40", Input: ` mkfontscale.x86_64 1.2.2-6.fc40 @fedora` }, { Name: "moby-engine", Version: "24.0.5-4.fc40", Input: ` moby-engine.x86_64 24.0.5-4.fc40 @fedora` }, { Name: "mpdecimal", Version: "2.5.1-9.fc40", Input: ` mpdecimal.x86_64 2.5.1-9.fc40 @2c98ee0d5281429683938d93eb2a9aa4` }, { Name: "mpfr", Version: "4.2.1-3.fc40", Input: ` mpfr.x86_64 4.2.1-3.fc40 @2c98ee0d5281429683938d93eb2a9aa4` }, { Name: "mpg123-libs", Version: "1.31.3-4.fc40", Input: ` mpg123-libs.x86_64 1.31.3-4.fc40 @fedora` }, { Name: "ncurses", Version: "6.4-12.20240127.fc40", Input: ` ncurses.x86_64 6.4-12.20240127.fc40 @fedora` }, { Name: "ncurses-base", Version: "6.4-12.20240127.fc40", Input: ` ncurses-base.noarch 6.4-12.20240127.fc40 @2c98ee0d5281429683938d93eb2a9aa4` }, { Name: "ncurses-libs", Version: "6.4-12.20240127.fc40", Input: ` ncurses-libs.x86_64 6.4-12.20240127.fc40 @2c98ee0d5281429683938d93eb2a9aa4` }, { Name: "net-tools", Version: "2.0-0.69.20160912git.fc40", Input: ` net-tools.x86_64 2.0-0.69.20160912git.fc40 @fedora` }, { Name: "nettle", Version: "3.9.1-6.fc40", Input: ` nettle.x86_64 3.9.1-6.fc40 @2c98ee0d5281429683938d93eb2a9aa4` }, { Name: "nftables", Version: "1.0.9-3.fc40", Input: ` nftables.x86_64 1:1.0.9-3.fc40 @fedora` }, { Name: "npth", Version: "1.7-1.fc40", Input: ` npth.x86_64 1.7-1.fc40 @2c98ee0d5281429683938d93eb2a9aa4` }, { Name: "nspr", Version: "4.35.0-28.fc40", Input: ` nspr.x86_64 4.35.0-28.fc40 @updates` }, { Name: "nss", Version: "3.103.0-1.fc40", Input: ` nss.x86_64 3.103.0-1.fc40 @updates` }, { Name: "nss-softokn", Version: "3.103.0-1.fc40", Input: ` nss-softokn.x86_64 3.103.0-1.fc40 @updates` }, { Name: "nss-softokn-freebl", Version: "3.103.0-1.fc40", Input: ` nss-softokn-freebl.x86_64 3.103.0-1.fc40 @updates` }, { Name: "nss-sysinit", Version: "3.103.0-1.fc40", Input: ` nss-sysinit.x86_64 3.103.0-1.fc40 @updates` }, { Name: "nss-util", Version: "3.103.0-1.fc40", Input: ` nss-util.x86_64 3.103.0-1.fc40 @updates` }, { Name: "openjpeg2", Version: "2.5.2-1.fc40", Input: ` openjpeg2.x86_64 2.5.2-1.fc40 @fedora` }, { Name: "openldap", Version: "2.6.7-1.fc40", Input: ` openldap.x86_64 2.6.7-1.fc40 @2c98ee0d5281429683938d93eb2a9aa4` }, { Name: "openssh", Version: "9.6p1-1.fc40.4", Input: ` openssh.x86_64 9.6p1-1.fc40.4 @updates` }, { Name: "openssh-clients", Version: "9.6p1-1.fc40.4", Input: ` openssh-clients.x86_64 9.6p1-1.fc40.4 @updates` }, { Name: "openssl", Version: "3.2.2-3.fc40", Input: ` openssl.x86_64 1:3.2.2-3.fc40 @updates` }, { Name: "openssl-libs", Version: "3.2.2-3.fc40", Input: ` openssl-libs.x86_64 1:3.2.2-3.fc40 @updates` }, { Name: "opus", Version: "1.5.1-1.fc40", Input: ` opus.x86_64 1.5.1-1.fc40 @fedora` }, { Name: "orc", Version: "0.4.39-1.fc40", Input: ` orc.x86_64 0.4.39-1.fc40 @updates` }, }, } for _, pt := range SupportedPackageTypes { cmd := pt.NewReadCommand() for _, v := range packages[pt] { pkg := NewPackage() pkg.Name = v.Name assert.Nil(t, cmd.Extractor([]byte(v.Input), pkg)) slog.Info("TestCommandExtractor()", "packagetype", pt, "name", pkg.Name, "package", pkg) assert.Equal(t, v.Name, pkg.Name) assert.Equal(t, v.Version, pkg.Version) assert.Equal(t, "present", pkg.State) } } } func TestReadPackage(t *testing.T) { decl := ` name: vim version: latest type: apk ` p := NewPackage() assert.NotNil(t, p) m := &MockCommand{ CommandExists: func() error { return nil }, Executor: func(value any) ([]byte, error) { return nil, nil }, Extractor: func(output []byte, target any) error { p.Name = "vim" p.Version = "1.1.1" p.State = "present" return nil }, } loadErr := p.LoadDecl(decl) assert.Nil(t, loadErr) assert.Equal(t, "latest", p.Version) p.ReadCommand = (*command.Command)(m) yaml, readErr := p.Read(context.Background()) assert.Nil(t, readErr) assert.Greater(t, len(yaml), 0) slog.Info("read()", "yaml", yaml) assert.Equal(t, "1.1.1", p.Version) slog.Info("resource: ", "package", p) assert.Nil(t, p.Validate()) } func TestReadAptPackage(t *testing.T) { if ! PackageTypeApt.Exists() { t.Skip("Unsupported package type") } decl := ` name: vim required: ">1.1.1" type: apt ` p := NewPackage() assert.NotNil(t, p) loadErr := p.LoadDecl(decl) assert.Nil(t, loadErr) assert.Equal(t, ">1.1.1", p.Required) p.ReadCommand = NewAptReadCommand() p.ReadCommand.Executor = func(value any) ([]byte, error) { return []byte(` Package: vim Version: 1.2.2 `), nil } yaml, readErr := p.Read(context.Background()) assert.Nil(t, readErr) assert.Greater(t, len(yaml), 0) slog.Info("read()", "yaml", yaml) assert.Equal(t, "1.2.2", p.Version) assert.Nil(t, p.Validate()) } func TestReadPackageError(t *testing.T) { ctx := context.Background() expected := fmt.Sprintf(` name: missing state: absent type: %s `, SystemPackageType) decl := fmt.Sprintf(` name: missing type: %s `, SystemPackageType) p := NewPackage() assert.NotNil(t, p) loadErr := p.LoadDecl(decl) assert.Nil(t, loadErr) p.ReadCommand = SystemPackageType.NewReadCommand() /* p.ReadCommand.Executor = func(value any) ([]byte, error) { return []byte(``), fmt.Errorf("exit status 1 dpkg-query: package 'makef' is not installed and no information is available\nUse dpkg --info (= dpkg-deb --info) to examine archive files.\n") } */ p.ResolveId(ctx) yaml, readErr := p.Read(ctx) assert.ErrorIs(t, readErr, ErrResourceStateAbsent) assert.YAMLEq(t, expected, string(yaml)) slog.Info("read()", "yaml", yaml) assert.Equal(t, "", p.Version) assert.Nil(t, p.Validate()) } func TestCreatePackage(t *testing.T) { } func TestPackageSetURI(t *testing.T) { p := NewPackage() assert.NotNil(t, p) e := p.SetURI("package://" + "12345_key?type=apk") assert.Nil(t, e) assert.Equal(t, "package", p.Type()) assert.Equal(t, "12345_key", p.Name) } func TestReadDebPackage(t *testing.T) { if ! PackageTypeDeb.Exists() { t.Skip("Unsupported package type") } decl := ` name: vim source: vim-8.2.3995-1ubuntu2.17.deb type: deb ` p := NewPackage() assert.NotNil(t, p) loadErr := p.LoadDecl(decl) assert.Nil(t, loadErr) p.ReadCommand = NewDebReadCommand() p.ReadCommand.Executor = func(value any) ([]byte, error) { return []byte(` Package: vim Version: 1.2.2 `), nil } yaml, readErr := p.Read(context.Background()) assert.Nil(t, readErr) slog.Info("Package.Read()", "package", p) assert.Greater(t, len(yaml), 0) slog.Info("read()", "yaml", yaml) assert.Equal(t, "1.2.2", p.Version) assert.Nil(t, p.Validate()) } func TestPackageTypeErr(t *testing.T) { decl := ` name: vim source: vim-8.2.3995-1ubuntu2.17.deb type: deb ` p := NewPackage() assert.NotNil(t, p) loadErr := p.LoadDecl(decl) assert.Nil(t, loadErr) p.ReadCommand = NewDebReadCommand() p.ReadCommand.CommandExists = func() error { return command.ErrUnknownCommand } p.ReadCommand.Executor = func(value any) ([]byte, error) { return []byte(` Package: vim Version: 1.2.2 `), nil } _, readErr := p.Read(context.Background()) assert.ErrorIs(t, readErr, ErrUnsupportedPackageType) } func TestPackageSourceRef(t *testing.T) { decl := ` name: vim sourceref: https://localhost/vim-8.2.3995-1ubuntu2.17.deb type: deb ` p := NewPackage() assert.NotNil(t, p) loadErr := p.LoadDecl(decl) assert.Nil(t, loadErr) p.ReadCommand = NewDebReadCommand() p.ReadCommand.CommandExists = func() error { return command.ErrUnknownCommand } p.ReadCommand.Executor = func(value any) ([]byte, error) { return []byte(` Package: vim Version: 1.2.2 `), nil } _, readErr := p.Read(context.Background()) assert.ErrorIs(t, readErr, ErrUnsupportedPackageType) }