mirror of
https://github.com/gogrlx/snack.git
synced 2026-04-01 20:58:42 -07:00
Merge pull request #11 from gogrlx/cd/bsd-pkg-ports
feat: add pkg (FreeBSD) and ports (OpenBSD) package managers
This commit is contained in:
141
pkg/pkg_test.go
Normal file
141
pkg/pkg_test.go
Normal file
@@ -0,0 +1,141 @@
|
||||
package pkg
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/gogrlx/snack"
|
||||
)
|
||||
|
||||
func TestParseQuery(t *testing.T) {
|
||||
input := "nginx\t1.24.0\tRobust and small WWW server\ncurl\t8.5.0\tCommand line tool for transferring data\n"
|
||||
pkgs := parseQuery(input)
|
||||
if len(pkgs) != 2 {
|
||||
t.Fatalf("expected 2 packages, got %d", len(pkgs))
|
||||
}
|
||||
if pkgs[0].Name != "nginx" || pkgs[0].Version != "1.24.0" {
|
||||
t.Errorf("unexpected first package: %+v", pkgs[0])
|
||||
}
|
||||
if pkgs[0].Description != "Robust and small WWW server" {
|
||||
t.Errorf("unexpected description: %q", pkgs[0].Description)
|
||||
}
|
||||
if !pkgs[0].Installed {
|
||||
t.Error("expected Installed=true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseSearch(t *testing.T) {
|
||||
input := `nginx-1.24.0 Robust and small WWW server
|
||||
curl-8.5.0 Command line tool for transferring data
|
||||
`
|
||||
pkgs := parseSearch(input)
|
||||
if len(pkgs) != 2 {
|
||||
t.Fatalf("expected 2 packages, got %d", len(pkgs))
|
||||
}
|
||||
if pkgs[0].Name != "nginx" || pkgs[0].Version != "1.24.0" {
|
||||
t.Errorf("unexpected first package: %+v", pkgs[0])
|
||||
}
|
||||
if pkgs[1].Name != "curl" || pkgs[1].Version != "8.5.0" {
|
||||
t.Errorf("unexpected second package: %+v", pkgs[1])
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseInfo(t *testing.T) {
|
||||
input := `Name : nginx
|
||||
Version : 1.24.0
|
||||
Comment : Robust and small WWW server
|
||||
Arch : FreeBSD:14:amd64
|
||||
`
|
||||
pkg := parseInfo(input)
|
||||
if pkg == nil {
|
||||
t.Fatal("expected non-nil package")
|
||||
}
|
||||
if pkg.Name != "nginx" {
|
||||
t.Errorf("expected name 'nginx', got %q", pkg.Name)
|
||||
}
|
||||
if pkg.Version != "1.24.0" {
|
||||
t.Errorf("unexpected version: %q", pkg.Version)
|
||||
}
|
||||
if pkg.Description != "Robust and small WWW server" {
|
||||
t.Errorf("unexpected description: %q", pkg.Description)
|
||||
}
|
||||
if pkg.Arch != "FreeBSD:14:amd64" {
|
||||
t.Errorf("unexpected arch: %q", pkg.Arch)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseUpgrades(t *testing.T) {
|
||||
input := `Updating FreeBSD repository catalogue...
|
||||
The following 2 package(s) will be affected:
|
||||
|
||||
Upgrading nginx: 1.24.0 -> 1.26.0
|
||||
Upgrading curl: 8.5.0 -> 8.6.0
|
||||
|
||||
Number of packages to be upgraded: 2
|
||||
`
|
||||
pkgs := parseUpgrades(input)
|
||||
if len(pkgs) != 2 {
|
||||
t.Fatalf("expected 2 packages, got %d", len(pkgs))
|
||||
}
|
||||
if pkgs[0].Name != "nginx" || pkgs[0].Version != "1.26.0" {
|
||||
t.Errorf("unexpected first package: %+v", pkgs[0])
|
||||
}
|
||||
if pkgs[1].Name != "curl" || pkgs[1].Version != "8.6.0" {
|
||||
t.Errorf("unexpected second package: %+v", pkgs[1])
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseFileList(t *testing.T) {
|
||||
input := `nginx-1.24.0:
|
||||
/usr/local/sbin/nginx
|
||||
/usr/local/etc/nginx/nginx.conf
|
||||
/usr/local/share/doc/nginx/README
|
||||
`
|
||||
files := parseFileList(input)
|
||||
if len(files) != 3 {
|
||||
t.Fatalf("expected 3 files, got %d", len(files))
|
||||
}
|
||||
if files[0] != "/usr/local/sbin/nginx" {
|
||||
t.Errorf("unexpected file: %q", files[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseOwner(t *testing.T) {
|
||||
input := "/usr/local/sbin/nginx was installed by package nginx-1.24.0\n"
|
||||
name := parseOwner(input)
|
||||
if name != "nginx" {
|
||||
t.Errorf("expected 'nginx', got %q", name)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSplitNameVersion(t *testing.T) {
|
||||
tests := []struct {
|
||||
input string
|
||||
wantName string
|
||||
wantVersion string
|
||||
}{
|
||||
{"nginx-1.24.0", "nginx", "1.24.0"},
|
||||
{"py39-pip-23.1", "py39-pip", "23.1"},
|
||||
{"bash", "bash", ""},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
name, ver := splitNameVersion(tt.input)
|
||||
if name != tt.wantName || ver != tt.wantVersion {
|
||||
t.Errorf("splitNameVersion(%q) = (%q, %q), want (%q, %q)",
|
||||
tt.input, name, ver, tt.wantName, tt.wantVersion)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestInterfaceCompliance(t *testing.T) {
|
||||
var _ snack.Manager = (*Pkg)(nil)
|
||||
var _ snack.VersionQuerier = (*Pkg)(nil)
|
||||
var _ snack.Cleaner = (*Pkg)(nil)
|
||||
var _ snack.FileOwner = (*Pkg)(nil)
|
||||
}
|
||||
|
||||
func TestName(t *testing.T) {
|
||||
p := New()
|
||||
if p.Name() != "pkg" {
|
||||
t.Errorf("Name() = %q, want %q", p.Name(), "pkg")
|
||||
}
|
||||
}
|
||||
116
ports/parse.go
Normal file
116
ports/parse.go
Normal file
@@ -0,0 +1,116 @@
|
||||
package ports
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/gogrlx/snack"
|
||||
)
|
||||
|
||||
// parseList parses the output of `pkg_info`.
|
||||
// Format: "name-version description text"
|
||||
func parseList(output string) []snack.Package {
|
||||
var pkgs []snack.Package
|
||||
for _, line := range strings.Split(output, "\n") {
|
||||
line = strings.TrimSpace(line)
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
// First field is name-version, rest is description
|
||||
parts := strings.SplitN(line, " ", 2)
|
||||
nameVer := parts[0]
|
||||
name, ver := splitNameVersion(nameVer)
|
||||
p := snack.Package{
|
||||
Name: name,
|
||||
Version: ver,
|
||||
Installed: true,
|
||||
}
|
||||
if len(parts) == 2 {
|
||||
p.Description = strings.TrimSpace(parts[1])
|
||||
}
|
||||
pkgs = append(pkgs, p)
|
||||
}
|
||||
return pkgs
|
||||
}
|
||||
|
||||
// parseSearchResults parses the output of `pkg_info -Q <query>`.
|
||||
// Each line is a package name-version.
|
||||
func parseSearchResults(output string) []snack.Package {
|
||||
var pkgs []snack.Package
|
||||
for _, line := range strings.Split(output, "\n") {
|
||||
line = strings.TrimSpace(line)
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
name, ver := splitNameVersion(line)
|
||||
pkgs = append(pkgs, snack.Package{
|
||||
Name: name,
|
||||
Version: ver,
|
||||
})
|
||||
}
|
||||
return pkgs
|
||||
}
|
||||
|
||||
// parseInfoOutput parses `pkg_info <pkg>` output.
|
||||
// The first line is typically "Information for name-version" or
|
||||
// the package description block. We extract name/version from
|
||||
// the stem or the provided pkg name.
|
||||
func parseInfoOutput(output string, pkg string) *snack.Package {
|
||||
lines := strings.Split(output, "\n")
|
||||
p := &snack.Package{Installed: true}
|
||||
|
||||
for _, line := range lines {
|
||||
line = strings.TrimSpace(line)
|
||||
if strings.HasPrefix(line, "Information for ") {
|
||||
nameVer := strings.TrimPrefix(line, "Information for ")
|
||||
nameVer = strings.TrimSuffix(nameVer, ":")
|
||||
p.Name, p.Version = splitNameVersion(nameVer)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// If we got description lines (after the header), join them
|
||||
if p.Name != "" {
|
||||
var desc []string
|
||||
inDesc := false
|
||||
for _, line := range lines {
|
||||
trimmed := strings.TrimSpace(line)
|
||||
if strings.HasPrefix(trimmed, "Information for ") {
|
||||
inDesc = false
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(trimmed, "Comment:") {
|
||||
p.Description = strings.TrimSpace(strings.TrimPrefix(trimmed, "Comment:"))
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(trimmed, "Description:") {
|
||||
inDesc = true
|
||||
continue
|
||||
}
|
||||
if inDesc && trimmed != "" {
|
||||
desc = append(desc, trimmed)
|
||||
}
|
||||
}
|
||||
if p.Description == "" && len(desc) > 0 {
|
||||
p.Description = strings.Join(desc, " ")
|
||||
}
|
||||
}
|
||||
|
||||
if p.Name == "" {
|
||||
// Fallback: try to parse from pkg argument
|
||||
p.Name, p.Version = splitNameVersion(pkg)
|
||||
if p.Name == "" {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
// splitNameVersion splits "name-version" at the last hyphen.
|
||||
// OpenBSD packages use the last hyphen before a version number as separator.
|
||||
func splitNameVersion(s string) (string, string) {
|
||||
idx := strings.LastIndex(s, "-")
|
||||
if idx <= 0 {
|
||||
return s, ""
|
||||
}
|
||||
return s[:idx], s[idx+1:]
|
||||
}
|
||||
@@ -1,2 +1,85 @@
|
||||
// Package ports provides Go bindings for OpenBSD ports/packages.
|
||||
package ports
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gogrlx/snack"
|
||||
)
|
||||
|
||||
// Ports wraps the OpenBSD pkg_add/pkg_delete/pkg_info CLI tools.
|
||||
type Ports struct {
|
||||
snack.Locker
|
||||
}
|
||||
|
||||
// New returns a new Ports manager.
|
||||
func New() *Ports {
|
||||
return &Ports{}
|
||||
}
|
||||
|
||||
// Name returns "ports".
|
||||
func (p *Ports) Name() string { return "ports" }
|
||||
|
||||
// Available reports whether pkg_add is present on the system.
|
||||
func (p *Ports) Available() bool { return available() }
|
||||
|
||||
// Install one or more packages.
|
||||
func (p *Ports) Install(ctx context.Context, pkgs []snack.Target, opts ...snack.Option) error {
|
||||
p.Lock()
|
||||
defer p.Unlock()
|
||||
return install(ctx, pkgs, opts...)
|
||||
}
|
||||
|
||||
// Remove one or more packages.
|
||||
func (p *Ports) Remove(ctx context.Context, pkgs []snack.Target, opts ...snack.Option) error {
|
||||
p.Lock()
|
||||
defer p.Unlock()
|
||||
return remove(ctx, pkgs, opts...)
|
||||
}
|
||||
|
||||
// Purge removes packages and cleans up dependencies.
|
||||
func (p *Ports) Purge(ctx context.Context, pkgs []snack.Target, opts ...snack.Option) error {
|
||||
p.Lock()
|
||||
defer p.Unlock()
|
||||
return purge(ctx, pkgs, opts...)
|
||||
}
|
||||
|
||||
// Upgrade all installed packages.
|
||||
func (p *Ports) Upgrade(ctx context.Context, opts ...snack.Option) error {
|
||||
p.Lock()
|
||||
defer p.Unlock()
|
||||
return upgrade(ctx, opts...)
|
||||
}
|
||||
|
||||
// Update is a no-op on OpenBSD (updates via fw_update or syspatch).
|
||||
func (p *Ports) Update(ctx context.Context) error {
|
||||
return update(ctx)
|
||||
}
|
||||
|
||||
// List returns all installed packages.
|
||||
func (p *Ports) List(ctx context.Context) ([]snack.Package, error) {
|
||||
return list(ctx)
|
||||
}
|
||||
|
||||
// Search queries for packages matching the query.
|
||||
func (p *Ports) Search(ctx context.Context, query string) ([]snack.Package, error) {
|
||||
return search(ctx, query)
|
||||
}
|
||||
|
||||
// Info returns details about a specific package.
|
||||
func (p *Ports) Info(ctx context.Context, pkg string) (*snack.Package, error) {
|
||||
return info(ctx, pkg)
|
||||
}
|
||||
|
||||
// IsInstalled reports whether a package is currently installed.
|
||||
func (p *Ports) IsInstalled(ctx context.Context, pkg string) (bool, error) {
|
||||
return isInstalled(ctx, pkg)
|
||||
}
|
||||
|
||||
// Version returns the installed version of a package.
|
||||
func (p *Ports) Version(ctx context.Context, pkg string) (string, error) {
|
||||
return version(ctx, pkg)
|
||||
}
|
||||
|
||||
// Verify interface compliance at compile time.
|
||||
var _ snack.Manager = (*Ports)(nil)
|
||||
|
||||
136
ports/ports_openbsd.go
Normal file
136
ports/ports_openbsd.go
Normal file
@@ -0,0 +1,136 @@
|
||||
//go:build openbsd
|
||||
|
||||
package ports
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"github.com/gogrlx/snack"
|
||||
)
|
||||
|
||||
func available() bool {
|
||||
_, err := exec.LookPath("pkg_add")
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func runCmd(ctx context.Context, name string, args []string, opts snack.Options) (string, error) {
|
||||
cmdName := name
|
||||
cmdArgs := make([]string, 0, len(args)+2)
|
||||
cmdArgs = append(cmdArgs, args...)
|
||||
|
||||
if opts.Sudo {
|
||||
cmdArgs = append([]string{cmdName}, cmdArgs...)
|
||||
cmdName = "sudo"
|
||||
}
|
||||
|
||||
c := exec.CommandContext(ctx, cmdName, cmdArgs...)
|
||||
var stdout, stderr bytes.Buffer
|
||||
c.Stdout = &stdout
|
||||
c.Stderr = &stderr
|
||||
err := c.Run()
|
||||
if err != nil {
|
||||
se := stderr.String()
|
||||
if strings.Contains(se, "permission denied") || strings.Contains(se, "need root") {
|
||||
return "", fmt.Errorf("ports: %w", snack.ErrPermissionDenied)
|
||||
}
|
||||
return "", fmt.Errorf("ports: %s: %w", strings.TrimSpace(se), err)
|
||||
}
|
||||
return stdout.String(), nil
|
||||
}
|
||||
|
||||
func install(ctx context.Context, pkgs []snack.Target, opts ...snack.Option) error {
|
||||
o := snack.ApplyOptions(opts...)
|
||||
args := snack.TargetNames(pkgs)
|
||||
_, err := runCmd(ctx, "pkg_add", args, o)
|
||||
return err
|
||||
}
|
||||
|
||||
func remove(ctx context.Context, pkgs []snack.Target, opts ...snack.Option) error {
|
||||
o := snack.ApplyOptions(opts...)
|
||||
args := snack.TargetNames(pkgs)
|
||||
_, err := runCmd(ctx, "pkg_delete", args, o)
|
||||
return err
|
||||
}
|
||||
|
||||
func purge(ctx context.Context, pkgs []snack.Target, opts ...snack.Option) error {
|
||||
o := snack.ApplyOptions(opts...)
|
||||
args := append([]string{"-c"}, snack.TargetNames(pkgs)...)
|
||||
_, err := runCmd(ctx, "pkg_delete", args, o)
|
||||
return err
|
||||
}
|
||||
|
||||
func upgrade(ctx context.Context, opts ...snack.Option) error {
|
||||
o := snack.ApplyOptions(opts...)
|
||||
_, err := runCmd(ctx, "pkg_add", []string{"-u"}, o)
|
||||
return err
|
||||
}
|
||||
|
||||
func update(_ context.Context) error {
|
||||
// No-op on OpenBSD; updates handled via fw_update or syspatch.
|
||||
return nil
|
||||
}
|
||||
|
||||
func list(ctx context.Context) ([]snack.Package, error) {
|
||||
out, err := runCmd(ctx, "pkg_info", nil, snack.Options{})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ports list: %w", err)
|
||||
}
|
||||
return parseList(out), nil
|
||||
}
|
||||
|
||||
func search(ctx context.Context, query string) ([]snack.Package, error) {
|
||||
out, err := runCmd(ctx, "pkg_info", []string{"-Q", query}, snack.Options{})
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "exit status 1") {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, fmt.Errorf("ports search: %w", err)
|
||||
}
|
||||
return parseSearchResults(out), nil
|
||||
}
|
||||
|
||||
func info(ctx context.Context, pkg string) (*snack.Package, error) {
|
||||
out, err := runCmd(ctx, "pkg_info", []string{pkg}, snack.Options{})
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "exit status 1") {
|
||||
return nil, fmt.Errorf("ports info %s: %w", pkg, snack.ErrNotFound)
|
||||
}
|
||||
return nil, fmt.Errorf("ports info: %w", err)
|
||||
}
|
||||
p := parseInfoOutput(out, pkg)
|
||||
if p == nil {
|
||||
return nil, fmt.Errorf("ports info %s: %w", pkg, snack.ErrNotFound)
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func isInstalled(ctx context.Context, pkg string) (bool, error) {
|
||||
c := exec.CommandContext(ctx, "pkg_info", pkg)
|
||||
err := c.Run()
|
||||
if err != nil {
|
||||
if exitErr, ok := err.(*exec.ExitError); ok && exitErr.ExitCode() != 0 {
|
||||
return false, nil
|
||||
}
|
||||
return false, fmt.Errorf("ports isInstalled: %w", err)
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func version(ctx context.Context, pkg string) (string, error) {
|
||||
out, err := runCmd(ctx, "pkg_info", []string{pkg}, snack.Options{})
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "exit status 1") {
|
||||
return "", fmt.Errorf("ports version %s: %w", pkg, snack.ErrNotInstalled)
|
||||
}
|
||||
return "", fmt.Errorf("ports version: %w", err)
|
||||
}
|
||||
p := parseInfoOutput(out, pkg)
|
||||
if p == nil || p.Version == "" {
|
||||
return "", fmt.Errorf("ports version %s: %w", pkg, snack.ErrNotInstalled)
|
||||
}
|
||||
return p.Version, nil
|
||||
}
|
||||
51
ports/ports_other.go
Normal file
51
ports/ports_other.go
Normal file
@@ -0,0 +1,51 @@
|
||||
//go:build !openbsd
|
||||
|
||||
package ports
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gogrlx/snack"
|
||||
)
|
||||
|
||||
func available() bool { return false }
|
||||
|
||||
func install(_ context.Context, _ []snack.Target, _ ...snack.Option) error {
|
||||
return snack.ErrUnsupportedPlatform
|
||||
}
|
||||
|
||||
func remove(_ context.Context, _ []snack.Target, _ ...snack.Option) error {
|
||||
return snack.ErrUnsupportedPlatform
|
||||
}
|
||||
|
||||
func purge(_ context.Context, _ []snack.Target, _ ...snack.Option) error {
|
||||
return snack.ErrUnsupportedPlatform
|
||||
}
|
||||
|
||||
func upgrade(_ context.Context, _ ...snack.Option) error {
|
||||
return snack.ErrUnsupportedPlatform
|
||||
}
|
||||
|
||||
func update(_ context.Context) error {
|
||||
return snack.ErrUnsupportedPlatform
|
||||
}
|
||||
|
||||
func list(_ context.Context) ([]snack.Package, error) {
|
||||
return nil, snack.ErrUnsupportedPlatform
|
||||
}
|
||||
|
||||
func search(_ context.Context, _ string) ([]snack.Package, error) {
|
||||
return nil, snack.ErrUnsupportedPlatform
|
||||
}
|
||||
|
||||
func info(_ context.Context, _ string) (*snack.Package, error) {
|
||||
return nil, snack.ErrUnsupportedPlatform
|
||||
}
|
||||
|
||||
func isInstalled(_ context.Context, _ string) (bool, error) {
|
||||
return false, snack.ErrUnsupportedPlatform
|
||||
}
|
||||
|
||||
func version(_ context.Context, _ string) (string, error) {
|
||||
return "", snack.ErrUnsupportedPlatform
|
||||
}
|
||||
113
ports/ports_test.go
Normal file
113
ports/ports_test.go
Normal file
@@ -0,0 +1,113 @@
|
||||
package ports
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/gogrlx/snack"
|
||||
)
|
||||
|
||||
func TestParseList(t *testing.T) {
|
||||
input := `bash-5.2.21 GNU Bourne Again Shell
|
||||
curl-8.5.0 command line tool for transferring data
|
||||
python-3.11.7p0 interpreted object-oriented programming language
|
||||
`
|
||||
pkgs := parseList(input)
|
||||
if len(pkgs) != 3 {
|
||||
t.Fatalf("expected 3 packages, got %d", len(pkgs))
|
||||
}
|
||||
if pkgs[0].Name != "bash" || pkgs[0].Version != "5.2.21" {
|
||||
t.Errorf("unexpected first package: %+v", pkgs[0])
|
||||
}
|
||||
if pkgs[0].Description != "GNU Bourne Again Shell" {
|
||||
t.Errorf("unexpected description: %q", pkgs[0].Description)
|
||||
}
|
||||
if !pkgs[0].Installed {
|
||||
t.Error("expected Installed=true")
|
||||
}
|
||||
if pkgs[2].Name != "python" || pkgs[2].Version != "3.11.7p0" {
|
||||
t.Errorf("unexpected third package: %+v", pkgs[2])
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseSearchResults(t *testing.T) {
|
||||
input := `nginx-1.24.0
|
||||
nginx-1.25.3
|
||||
`
|
||||
pkgs := parseSearchResults(input)
|
||||
if len(pkgs) != 2 {
|
||||
t.Fatalf("expected 2 packages, got %d", len(pkgs))
|
||||
}
|
||||
if pkgs[0].Name != "nginx" || pkgs[0].Version != "1.24.0" {
|
||||
t.Errorf("unexpected first package: %+v", pkgs[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseInfoOutput(t *testing.T) {
|
||||
input := `Information for nginx-1.24.0:
|
||||
|
||||
Comment:
|
||||
robust and small WWW server
|
||||
Description:
|
||||
nginx is an HTTP and reverse proxy server, a mail proxy server,
|
||||
and a generic TCP/UDP proxy server.
|
||||
`
|
||||
pkg := parseInfoOutput(input, "nginx-1.24.0")
|
||||
if pkg == nil {
|
||||
t.Fatal("expected non-nil package")
|
||||
}
|
||||
if pkg.Name != "nginx" {
|
||||
t.Errorf("expected name 'nginx', got %q", pkg.Name)
|
||||
}
|
||||
if pkg.Version != "1.24.0" {
|
||||
t.Errorf("unexpected version: %q", pkg.Version)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseInfoOutputWithComment(t *testing.T) {
|
||||
input := `Information for curl-8.5.0:
|
||||
|
||||
Comment: command line tool for transferring data
|
||||
Description:
|
||||
curl is a tool to transfer data from or to a server.
|
||||
`
|
||||
pkg := parseInfoOutput(input, "curl-8.5.0")
|
||||
if pkg == nil {
|
||||
t.Fatal("expected non-nil package")
|
||||
}
|
||||
if pkg.Name != "curl" {
|
||||
t.Errorf("expected name 'curl', got %q", pkg.Name)
|
||||
}
|
||||
if pkg.Description != "command line tool for transferring data" {
|
||||
t.Errorf("unexpected description: %q", pkg.Description)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSplitNameVersion(t *testing.T) {
|
||||
tests := []struct {
|
||||
input string
|
||||
wantName string
|
||||
wantVersion string
|
||||
}{
|
||||
{"nginx-1.24.0", "nginx", "1.24.0"},
|
||||
{"py3-pip-23.1", "py3-pip", "23.1"},
|
||||
{"bash", "bash", ""},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
name, ver := splitNameVersion(tt.input)
|
||||
if name != tt.wantName || ver != tt.wantVersion {
|
||||
t.Errorf("splitNameVersion(%q) = (%q, %q), want (%q, %q)",
|
||||
tt.input, name, ver, tt.wantName, tt.wantVersion)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestInterfaceCompliance(t *testing.T) {
|
||||
var _ snack.Manager = (*Ports)(nil)
|
||||
}
|
||||
|
||||
func TestName(t *testing.T) {
|
||||
p := New()
|
||||
if p.Name() != "ports" {
|
||||
t.Errorf("Name() = %q, want %q", p.Name(), "ports")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user