mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-11-15 04:53:00 +08:00
177 lines
6.9 KiB
Go
177 lines
6.9 KiB
Go
package update
|
|
|
|
import (
|
|
"encoding/json"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/matthewlu070111/smart-srun/core/internal/domain"
|
|
)
|
|
|
|
func fixtureManifest(manager string, kinds ...string) Manifest {
|
|
m := Manifest{SchemaVersion: 1, Release: "2.0.0rc10", Channel: "rc", SourceCommit: strings.Repeat("a", 40)}
|
|
for _, kind := range kinds {
|
|
a := Asset{ID: kind + "-" + manager, Kind: kind, PackageManager: manager,
|
|
OpenWrtArch: "x86_64", Format: "ipk", PackageVersion: "2.0.0~rc10-r1", SDKRelease: "24.10.8",
|
|
Target: "x86/64", GOOS: "linux", GOARCH: "amd64", SHA256: strings.Repeat("b", 64),
|
|
Bytes: 2048, InstalledBytes: 4096, FirmwareCompat: []string{"24.10"}, Validation: Validation{Build: true, ELF: true}}
|
|
if manager == "apk" {
|
|
a.Format, a.PackageVersion, a.SDKRelease, a.FirmwareCompat = "apk", "2.0.0_rc10-r1", "25.12.2", []string{"25.12"}
|
|
}
|
|
if kind == "luci" {
|
|
a.OpenWrtArch = "all"
|
|
if manager == "apk" {
|
|
a.OpenWrtArch = "noarch"
|
|
}
|
|
a.Validation.ELF = false
|
|
}
|
|
a.URL = "https://github.com/" + Repository + "/releases/download/" + m.Release + "/" + a.PackageName() + "-fixture." + a.Format
|
|
if a.Format == "ipk" {
|
|
a.URL = strings.Replace(a.URL, a.PackageName()+"-fixture", a.PackageName()+"_fixture", 1)
|
|
}
|
|
m.Assets = append(m.Assets, a)
|
|
}
|
|
return m
|
|
}
|
|
|
|
func fixtureInventory(manager, mode string) Inventory {
|
|
i := Inventory{PackageManager: manager, Architecture: "x86_64", FirmwareFamily: "24.10",
|
|
DisplayVersion: "2.0.0rc2", Packages: map[string]string{}}
|
|
native := "2.0.0~rc2-r1"
|
|
if manager == "apk" {
|
|
i.FirmwareFamily, native = "25.12", "2.0.0_rc2-r1"
|
|
}
|
|
if mode == "bundle" {
|
|
i.Packages["luci-app-smart-srun-bundle"] = native
|
|
} else {
|
|
i.Packages["smart-srun"] = native
|
|
if mode == "split" {
|
|
i.Packages["luci-app-smart-srun"] = native
|
|
}
|
|
}
|
|
return i
|
|
}
|
|
|
|
func TestVersionNumericRCAndStableOrdering(t *testing.T) {
|
|
values := []string{"1.9.9", "2.0.0rc2", "2.0.0rc10", "2.0.0", "2.0.1rc1", "2.1.0", "3.0.0"}
|
|
for left := range values {
|
|
a, err := ParseVersion(values[left])
|
|
if err != nil || a.String() != values[left] {
|
|
t.Fatalf("parse %q: %v", values[left], err)
|
|
}
|
|
for right := range values {
|
|
b, _ := ParseVersion(values[right])
|
|
order := a.Compare(b)
|
|
if (left < right && order >= 0) || (left == right && order != 0) || (left > right && order <= 0) {
|
|
t.Fatalf("%v vs %v: %d", a, b, order)
|
|
}
|
|
}
|
|
}
|
|
for _, text := range []string{"v2.0.0", "2.0.0rc0", "2.0.0rc01", "2.0.0-beta1", "02.0.0", "2.0.0\n", "4294967296.0.0", "2.0.0rc4294967296"} {
|
|
if _, err := ParseVersion(text); err == nil {
|
|
t.Fatalf("accepted invalid version %q", text)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestPlanPreservesNativeFormatArchitectureAndOwnership(t *testing.T) {
|
|
for _, manager := range []string{"opkg", "apk"} {
|
|
for _, mode := range []string{"core", "split", "bundle"} {
|
|
t.Run(manager+"/"+mode, func(t *testing.T) {
|
|
manifest := fixtureManifest(manager, "core", "luci", "bundle")
|
|
data, _ := json.Marshal(manifest)
|
|
parsed, err := ParseManifest(data)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
plan, err := BuildPlan(parsed, fixtureInventory(manager, mode), "")
|
|
if err != nil || plan.InstallMode != mode || len(plan.ID) != 64 {
|
|
t.Fatalf("plan: %+v, %v", plan, err)
|
|
}
|
|
count := 1
|
|
if mode == "split" {
|
|
count = 2
|
|
}
|
|
if len(plan.Assets) != count {
|
|
t.Fatal("wrong package set")
|
|
}
|
|
for _, asset := range plan.Assets {
|
|
if asset.PackageManager != manager || (mode == "bundle") != (asset.Kind == "bundle") {
|
|
t.Fatal("silently changed package format or ownership")
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestManifestRejectsAliasedFieldsDuplicatesAndForeignURLs(t *testing.T) {
|
|
data, _ := json.Marshal(fixtureManifest("opkg", "bundle"))
|
|
for name, bad := range map[string]string{
|
|
"duplicate": strings.Replace(string(data), `"kind":"bundle"`, `"kind":"bundle","kind":"core"`, 1),
|
|
"alias": strings.Replace(string(data), `"url":`, `"URL":`, 1),
|
|
"nested alias": strings.Replace(string(data), `"build":true`, `"Build":true`, 1),
|
|
"foreign repo": strings.Replace(string(data), Repository, "someone/smart-srun", 1),
|
|
"other release": strings.Replace(string(data), "/download/2.0.0rc10/", "/download/2.0.0rc2/", 1),
|
|
"http": strings.Replace(string(data), "https://", "http://", 1),
|
|
"escaped path": strings.Replace(string(data), "/download/", "/%64ownload/", 1),
|
|
"null": strings.Replace(string(data), `"kind":"bundle"`, `"kind":null`, 1),
|
|
"trailing": string(data) + "{}",
|
|
} {
|
|
t.Run(name, func(t *testing.T) {
|
|
if _, err := ParseManifest([]byte(bad)); err == nil {
|
|
t.Fatal("invalid manifest accepted")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestPlanRejectsAmbiguousAndIncompatibleInstallations(t *testing.T) {
|
|
for name, mutate := range map[string]func(*Manifest, *Inventory){
|
|
"wrong architecture": func(_ *Manifest, i *Inventory) { i.Architecture = "aarch64_cortex-a53" },
|
|
"wrong firmware": func(_ *Manifest, i *Inventory) { i.FirmwareFamily = "23.05" },
|
|
"wrong manager": func(_ *Manifest, i *Inventory) { i.PackageManager = "apk" },
|
|
"mixed ownership": func(_ *Manifest, i *Inventory) { i.Packages["luci-app-smart-srun-bundle"] = "2.0.0~rc2-r1" },
|
|
"half updated": func(_ *Manifest, i *Inventory) { i.Packages["luci-app-smart-srun"] = "2.0.0~rc1-r1" },
|
|
"different release counters": func(m *Manifest, _ *Inventory) { m.Assets[1].PackageVersion = "2.0.0~rc10-r2" },
|
|
"missing ELF check": func(m *Manifest, _ *Inventory) { m.Assets[0].Validation.ELF = false },
|
|
"combined size": func(m *Manifest, _ *Inventory) { m.Assets[0].InstalledBytes = MaxPayloadBytes },
|
|
"ambiguous": func(m *Manifest, _ *Inventory) {
|
|
a := m.Assets[0]
|
|
a.ID += "-other"
|
|
a.URL = strings.Replace(a.URL, "_fixture", "_second", 1)
|
|
m.Assets = append(m.Assets, a)
|
|
},
|
|
} {
|
|
t.Run(name, func(t *testing.T) {
|
|
m, i := fixtureManifest("opkg", "core", "luci"), fixtureInventory("opkg", "split")
|
|
mutate(&m, &i)
|
|
if _, err := BuildPlan(m, i, ""); err == nil {
|
|
t.Fatal("unsafe update plan accepted")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestStableDoesNotSilentlyChangeChannelOrDowngrade(t *testing.T) {
|
|
m := fixtureManifest("apk", "core")
|
|
i := fixtureInventory("apk", "core")
|
|
i.DisplayVersion, i.Packages["smart-srun"] = "2.0.0", "2.0.0-r1"
|
|
m.Release = "2.1.0rc10"
|
|
m.Assets[0].PackageVersion = "2.1.0_rc10-r1"
|
|
m.Assets[0].URL = strings.Replace(m.Assets[0].URL, "/2.0.0rc10/", "/2.1.0rc10/", 1)
|
|
if _, err := BuildPlan(m, i, ""); err == nil {
|
|
t.Fatal("stable implicitly selected RC")
|
|
}
|
|
if _, err := BuildPlan(m, i, "rc"); err != nil {
|
|
t.Fatalf("explicit RC channel was rejected: %v", err)
|
|
}
|
|
m = fixtureManifest("apk", "core")
|
|
i = fixtureInventory("apk", "core")
|
|
i.DisplayVersion, i.Packages["smart-srun"] = "2.0.0", "2.0.0-r1"
|
|
_, err := BuildPlan(m, i, "rc")
|
|
if code, _ := domain.CodeOf(err); code != domain.CodeNotFound {
|
|
t.Fatalf("RC below stable should not be an upgrade: %v", err)
|
|
}
|
|
}
|