mirror of
https://github.com/webinstall/webi-installers.git
synced 2026-04-06 18:36:50 +00:00
feat(resolve): add POSIX/ANYOS/ANYARCH matching and test coverage
The resolver now handles: - ANYOS assets match any query OS - posix_2017/posix_2024 assets match any non-Windows OS - ANYARCH assets match any query architecture (ranked below specific) 14 tests covering: exact match, version constraints, arch fallback (Rosetta 2, Windows ARM64, micro-arch), format preference, libc filtering, base-over-variant preference, POSIX/ANYOS/ANYARCH fallback, Survey catalog, and no-match.
This commit is contained in:
@@ -93,16 +93,16 @@ func Best(dists []Dist, q Query) *Match {
|
||||
continue
|
||||
}
|
||||
|
||||
// OS filter.
|
||||
if d.OS != string(q.OS) {
|
||||
// OS filter: exact match, POSIX fallback, or ANYOS.
|
||||
if !osMatches(q.OS, d.OS) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Arch filter (including compat arches).
|
||||
// Empty arch in the dist means "universal/platform-agnostic" — accept it
|
||||
// but rank it lower than an exact match.
|
||||
// Empty arch or ANYARCH means "universal/platform-agnostic" —
|
||||
// accept it but rank it lower than an exact match.
|
||||
aRank, archOK := archRank[d.Arch]
|
||||
if !archOK && d.Arch == "" {
|
||||
if !archOK && (d.Arch == "" || d.Arch == string(buildmeta.ArchAny)) {
|
||||
// Universal binary — rank after all specific arches.
|
||||
aRank = len(compatArches)
|
||||
archOK = true
|
||||
@@ -241,6 +241,25 @@ func (c *candidate) betterThan(other *candidate) bool {
|
||||
return c.formatRank < other.formatRank
|
||||
}
|
||||
|
||||
// osMatches checks whether a dist's OS is acceptable for the query.
|
||||
// Matches exact OS, ANYOS (universal), and POSIX compatibility levels
|
||||
// (posix_2017 matches any non-Windows OS).
|
||||
func osMatches(want buildmeta.OS, have string) bool {
|
||||
if have == string(want) {
|
||||
return true
|
||||
}
|
||||
if have == string(buildmeta.OSAny) {
|
||||
return true
|
||||
}
|
||||
// POSIX assets run on any non-Windows system.
|
||||
if want != buildmeta.OSWindows {
|
||||
if have == string(buildmeta.OSPosix2017) || have == string(buildmeta.OSPosix2024) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// libcMatches checks whether a dist's libc is acceptable for the query.
|
||||
func libcMatches(os buildmeta.OS, want buildmeta.Libc, have string) bool {
|
||||
// Darwin and Windows don't use libc tagging — accept anything.
|
||||
|
||||
250
internal/resolve/resolve_test.go
Normal file
250
internal/resolve/resolve_test.go
Normal file
@@ -0,0 +1,250 @@
|
||||
package resolve
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/webinstall/webi-installers/internal/buildmeta"
|
||||
)
|
||||
|
||||
// bat-style dists: standard goreleaser output.
|
||||
var batDists = []Dist{
|
||||
{Version: "0.26.1", Channel: "stable", OS: "darwin", Arch: "aarch64", Format: ".tar.gz", Filename: "bat-v0.26.1-aarch64-apple-darwin.tar.gz"},
|
||||
{Version: "0.26.1", Channel: "stable", OS: "darwin", Arch: "x86_64", Format: ".tar.gz", Filename: "bat-v0.26.1-x86_64-apple-darwin.tar.gz"},
|
||||
{Version: "0.26.1", Channel: "stable", OS: "linux", Arch: "aarch64", Libc: "gnu", Format: ".tar.gz", Filename: "bat-v0.26.1-aarch64-unknown-linux-gnu.tar.gz"},
|
||||
{Version: "0.26.1", Channel: "stable", OS: "linux", Arch: "aarch64", Libc: "musl", Format: ".tar.gz", Filename: "bat-v0.26.1-aarch64-unknown-linux-musl.tar.gz"},
|
||||
{Version: "0.26.1", Channel: "stable", OS: "linux", Arch: "x86_64", Libc: "gnu", Format: ".tar.gz", Filename: "bat-v0.26.1-x86_64-unknown-linux-gnu.tar.gz"},
|
||||
{Version: "0.26.1", Channel: "stable", OS: "linux", Arch: "x86_64", Libc: "musl", Format: ".tar.gz", Filename: "bat-v0.26.1-x86_64-unknown-linux-musl.tar.gz"},
|
||||
{Version: "0.26.1", Channel: "stable", OS: "windows", Arch: "aarch64", Format: ".zip", Filename: "bat-v0.26.1-aarch64-pc-windows-msvc.zip"},
|
||||
{Version: "0.26.1", Channel: "stable", OS: "windows", Arch: "x86_64", Libc: "gnu", Format: ".zip", Filename: "bat-v0.26.1-x86_64-pc-windows-gnu.zip"},
|
||||
{Version: "0.26.1", Channel: "stable", OS: "windows", Arch: "x86_64", Libc: "msvc", Format: ".zip", Filename: "bat-v0.26.1-x86_64-pc-windows-msvc.zip"},
|
||||
// Older version.
|
||||
{Version: "0.25.0", Channel: "stable", OS: "darwin", Arch: "aarch64", Format: ".tar.gz", Filename: "bat-v0.25.0-aarch64-apple-darwin.tar.gz"},
|
||||
{Version: "0.25.0", Channel: "stable", OS: "linux", Arch: "x86_64", Libc: "gnu", Format: ".tar.gz", Filename: "bat-v0.25.0-x86_64-unknown-linux-gnu.tar.gz"},
|
||||
}
|
||||
|
||||
func TestBestExactMatch(t *testing.T) {
|
||||
m := Best(batDists, Query{
|
||||
OS: buildmeta.OSLinux,
|
||||
Arch: buildmeta.ArchAMD64,
|
||||
Formats: []string{".tar.gz"},
|
||||
})
|
||||
if m == nil {
|
||||
t.Fatal("expected match")
|
||||
}
|
||||
if m.Version != "0.26.1" {
|
||||
t.Errorf("Version = %q, want 0.26.1", m.Version)
|
||||
}
|
||||
if m.Filename != "bat-v0.26.1-x86_64-unknown-linux-gnu.tar.gz" {
|
||||
t.Errorf("Filename = %q", m.Filename)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBestVersionConstraint(t *testing.T) {
|
||||
m := Best(batDists, Query{
|
||||
OS: buildmeta.OSDarwin,
|
||||
Arch: buildmeta.ArchARM64,
|
||||
Formats: []string{".tar.gz"},
|
||||
Version: "0.25",
|
||||
})
|
||||
if m == nil {
|
||||
t.Fatal("expected match")
|
||||
}
|
||||
if m.Version != "0.25.0" {
|
||||
t.Errorf("Version = %q, want 0.25.0", m.Version)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBestArchFallback(t *testing.T) {
|
||||
// macOS ARM64 should fall back to x86_64 via Rosetta 2
|
||||
// when no ARM64 build exists.
|
||||
dists := []Dist{
|
||||
{Version: "1.0.0", Channel: "stable", OS: "darwin", Arch: "x86_64", Format: ".tar.gz", Filename: "tool-darwin-amd64.tar.gz"},
|
||||
}
|
||||
m := Best(dists, Query{
|
||||
OS: buildmeta.OSDarwin,
|
||||
Arch: buildmeta.ArchARM64,
|
||||
Formats: []string{".tar.gz"},
|
||||
})
|
||||
if m == nil {
|
||||
t.Fatal("expected match via Rosetta 2 fallback")
|
||||
}
|
||||
if m.Arch != "x86_64" {
|
||||
t.Errorf("Arch = %q, want x86_64", m.Arch)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBestPrefersNativeOverCompat(t *testing.T) {
|
||||
// When both native and compat builds exist, prefer native.
|
||||
dists := []Dist{
|
||||
{Version: "1.0.0", Channel: "stable", OS: "darwin", Arch: "x86_64", Format: ".tar.gz", Filename: "tool-darwin-amd64.tar.gz"},
|
||||
{Version: "1.0.0", Channel: "stable", OS: "darwin", Arch: "aarch64", Format: ".tar.gz", Filename: "tool-darwin-arm64.tar.gz"},
|
||||
}
|
||||
m := Best(dists, Query{
|
||||
OS: buildmeta.OSDarwin,
|
||||
Arch: buildmeta.ArchARM64,
|
||||
Formats: []string{".tar.gz"},
|
||||
})
|
||||
if m == nil {
|
||||
t.Fatal("expected match")
|
||||
}
|
||||
if m.Arch != "aarch64" {
|
||||
t.Errorf("Arch = %q, want aarch64 (native)", m.Arch)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBestFormatPreference(t *testing.T) {
|
||||
dists := []Dist{
|
||||
{Version: "1.0.0", Channel: "stable", OS: "linux", Arch: "x86_64", Format: ".zip", Filename: "tool.zip"},
|
||||
{Version: "1.0.0", Channel: "stable", OS: "linux", Arch: "x86_64", Format: ".tar.gz", Filename: "tool.tar.gz"},
|
||||
{Version: "1.0.0", Channel: "stable", OS: "linux", Arch: "x86_64", Format: ".tar.xz", Filename: "tool.tar.xz"},
|
||||
}
|
||||
m := Best(dists, Query{
|
||||
OS: buildmeta.OSLinux,
|
||||
Arch: buildmeta.ArchAMD64,
|
||||
Formats: []string{".tar.xz", ".tar.gz", ".zip"},
|
||||
})
|
||||
if m == nil {
|
||||
t.Fatal("expected match")
|
||||
}
|
||||
if m.Format != ".tar.xz" {
|
||||
t.Errorf("Format = %q, want .tar.xz", m.Format)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBestNoMatch(t *testing.T) {
|
||||
m := Best(batDists, Query{
|
||||
OS: buildmeta.OSFreeBSD,
|
||||
Arch: buildmeta.ArchAMD64,
|
||||
Formats: []string{".tar.gz"},
|
||||
})
|
||||
if m != nil {
|
||||
t.Errorf("expected nil, got %+v", m)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBestLibcMusl(t *testing.T) {
|
||||
m := Best(batDists, Query{
|
||||
OS: buildmeta.OSLinux,
|
||||
Arch: buildmeta.ArchAMD64,
|
||||
Libc: buildmeta.LibcMusl,
|
||||
Formats: []string{".tar.gz"},
|
||||
})
|
||||
if m == nil {
|
||||
t.Fatal("expected match")
|
||||
}
|
||||
if m.Libc != "musl" {
|
||||
t.Errorf("Libc = %q, want musl", m.Libc)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBestPrefersBaseOverVariant(t *testing.T) {
|
||||
dists := []Dist{
|
||||
{Version: "1.0.0", Channel: "stable", OS: "linux", Arch: "x86_64", Format: ".tar.gz", Filename: "tool.tar.gz"},
|
||||
{Version: "1.0.0", Channel: "stable", OS: "linux", Arch: "x86_64", Format: ".tar.gz", Filename: "tool-rocm.tar.gz", Variants: []string{"rocm"}},
|
||||
}
|
||||
m := Best(dists, Query{
|
||||
OS: buildmeta.OSLinux,
|
||||
Arch: buildmeta.ArchAMD64,
|
||||
Formats: []string{".tar.gz"},
|
||||
})
|
||||
if m == nil {
|
||||
t.Fatal("expected match")
|
||||
}
|
||||
if m.Filename != "tool.tar.gz" {
|
||||
t.Errorf("got variant build %q, want base", m.Filename)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBestPosixFallback(t *testing.T) {
|
||||
dists := []Dist{
|
||||
{Version: "1.0.0", Channel: "stable", OS: "posix_2017", Format: ".tar.gz", Filename: "script.tar.gz"},
|
||||
}
|
||||
m := Best(dists, Query{
|
||||
OS: buildmeta.OSLinux,
|
||||
Arch: buildmeta.ArchAMD64,
|
||||
Formats: []string{".tar.gz"},
|
||||
})
|
||||
if m == nil {
|
||||
t.Fatal("expected match via POSIX fallback")
|
||||
}
|
||||
if m.OS != "posix_2017" {
|
||||
t.Errorf("OS = %q, want posix_2017", m.OS)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBestAnyOS(t *testing.T) {
|
||||
dists := []Dist{
|
||||
{Version: "1.0.0", Channel: "stable", OS: "ANYOS", Format: ".tar.gz", Filename: "tool.tar.gz"},
|
||||
}
|
||||
m := Best(dists, Query{
|
||||
OS: buildmeta.OSWindows,
|
||||
Arch: buildmeta.ArchAMD64,
|
||||
Formats: []string{".tar.gz"},
|
||||
})
|
||||
if m == nil {
|
||||
t.Fatal("expected match via ANYOS")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBestAnyArch(t *testing.T) {
|
||||
dists := []Dist{
|
||||
{Version: "1.0.0", Channel: "stable", OS: "linux", Arch: "ANYARCH", Format: ".tar.gz", Filename: "tool.tar.gz"},
|
||||
}
|
||||
m := Best(dists, Query{
|
||||
OS: buildmeta.OSLinux,
|
||||
Arch: buildmeta.ArchARM64,
|
||||
Formats: []string{".tar.gz"},
|
||||
})
|
||||
if m == nil {
|
||||
t.Fatal("expected match via ANYARCH")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBestWindowsArchFallback(t *testing.T) {
|
||||
// Windows ARM64 should fall back to x86_64 via emulation.
|
||||
dists := []Dist{
|
||||
{Version: "1.0.0", Channel: "stable", OS: "windows", Arch: "x86_64", Format: ".zip", Filename: "tool-win64.zip"},
|
||||
}
|
||||
m := Best(dists, Query{
|
||||
OS: buildmeta.OSWindows,
|
||||
Arch: buildmeta.ArchARM64,
|
||||
Formats: []string{".zip"},
|
||||
})
|
||||
if m == nil {
|
||||
t.Fatal("expected match via Windows ARM64 emulation")
|
||||
}
|
||||
if m.Arch != "x86_64" {
|
||||
t.Errorf("Arch = %q, want x86_64", m.Arch)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBestMicroArchFallback(t *testing.T) {
|
||||
// amd64v3 query should fall back to amd64 baseline.
|
||||
dists := []Dist{
|
||||
{Version: "1.0.0", Channel: "stable", OS: "linux", Arch: "x86_64", Format: ".tar.gz", Filename: "tool-amd64.tar.gz"},
|
||||
}
|
||||
m := Best(dists, Query{
|
||||
OS: buildmeta.OSLinux,
|
||||
Arch: buildmeta.ArchAMD64v3,
|
||||
Formats: []string{".tar.gz"},
|
||||
})
|
||||
if m == nil {
|
||||
t.Fatal("expected match via micro-arch fallback")
|
||||
}
|
||||
if m.Arch != "x86_64" {
|
||||
t.Errorf("Arch = %q, want x86_64 (baseline)", m.Arch)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSurvey(t *testing.T) {
|
||||
cat := Survey(batDists)
|
||||
if cat.Stable != "0.26.1" {
|
||||
t.Errorf("Stable = %q, want 0.26.1", cat.Stable)
|
||||
}
|
||||
if cat.Latest != "0.26.1" {
|
||||
t.Errorf("Latest = %q, want 0.26.1", cat.Latest)
|
||||
}
|
||||
if len(cat.OSes) != 3 {
|
||||
t.Errorf("OSes = %v, want 3", cat.OSes)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user