package main import ( "bytes" "crypto/ed25519" "crypto/rand" "encoding/base64" "encoding/json" "os" "path/filepath" "testing" "time" "gitgud.io/mike/mpv-manager/pkg/releasemanifest" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func verifierFixture(t *testing.T) (string, string) { t.Helper() publicKey, privateKey, err := ed25519.GenerateKey(rand.Reader) require.NoError(t, err) assets := make([]releasemanifest.Asset, 0, 6) for _, target := range [][2]string{ {"linux", "amd64"}, {"linux", "arm64"}, {"windows", "amd64"}, {"windows", "arm64"}, {"darwin", "amd64"}, {"darwin", "arm64"}, } { cpuBaseline := "arm64" if target[1] == "amd64" { cpuBaseline = "x86-64-v2" } assets = append(assets, releasemanifest.Asset{ GOOS: target[0], GOARCH: target[1], CPUBaseline: cpuBaseline, Format: releasemanifest.ManagerAssetFormat, URL: "https://example.test/" + target[0] + "-" + target[1], BLAKE3: "blake3:" + string(bytes.Repeat([]byte{'a'}, 64)), Size: 10, InstallScope: releasemanifest.ManagerInstallScope, UpdateStrategy: releasemanifest.ManagerUpdateStrategy, ExpectedProduct: "mpv-manager", ExpectedComponent: releasemanifest.ManagerPortableComponentID, }) } manifest := releasemanifest.Manifest{ SchemaVersion: releasemanifest.SchemaVersion, Channel: releasemanifest.StableChannel, PublishedAt: time.Now().UTC().Format(time.RFC3339), MinimumUpdaterVersion: "1.2.0", Version: "1.3.0", Components: map[string]releasemanifest.Component{ releasemanifest.ManagerPortableComponentID: {Version: "1.3.0", Assets: assets}, }, } for _, asset := range assets { switch asset.GOOS + "/" + asset.GOARCH { case "linux/amd64": manifest.Manager.LinuxAMD64.URL, manifest.Manager.LinuxAMD64.BLAKE3 = asset.URL, asset.BLAKE3 case "linux/arm64": manifest.Manager.LinuxARM64.URL, manifest.Manager.LinuxARM64.BLAKE3 = asset.URL, asset.BLAKE3 case "windows/amd64": manifest.Manager.WinX86_64.URL, manifest.Manager.WinX86_64.BLAKE3 = asset.URL, asset.BLAKE3 case "windows/arm64": manifest.Manager.WinARM64.URL, manifest.Manager.WinARM64.BLAKE3 = asset.URL, asset.BLAKE3 case "darwin/amd64": manifest.Manager.MacosIntel.URL, manifest.Manager.MacosIntel.BLAKE3 = asset.URL, asset.BLAKE3 case "darwin/arm64": manifest.Manager.MacosARM.URL, manifest.Manager.MacosARM.BLAKE3 = asset.URL, asset.BLAKE3 } } require.NoError(t, manifest.Sign("release-2026", privateKey)) data, err := json.Marshal(manifest) require.NoError(t, err) path := filepath.Join(t.TempDir(), "releases.json") require.NoError(t, os.WriteFile(path, data, 0600)) keySpec := "release-2026=" + base64.StdEncoding.EncodeToString(publicKey) return path, keySpec } func TestRunVerifiesSignedManifestAndExpectedVersion(t *testing.T) { path, keySpec := verifierFixture(t) var stdout, stderr bytes.Buffer err := run( []string{"-manifest", path, "-expected-version", "v1.3.0"}, &stdout, &stderr, func(name string) string { if name == manifestPublicKeysEnv { return keySpec } return "" }, ) require.NoError(t, err, stderr.String()) assert.Contains(t, stdout.String(), "Verified release manifest 1.3.0") } func TestRunRejectsWrongExpectedVersion(t *testing.T) { path, keySpec := verifierFixture(t) err := run( []string{"-manifest", path, "-expected-version", "1.4.0", "-trusted-keys", keySpec}, ioDiscard{}, ioDiscard{}, func(string) string { return "" }, ) require.Error(t, err) assert.Contains(t, err.Error(), "does not match") } func TestRunCanValidateOnlyTheTrustedKeyRing(t *testing.T) { _, keySpec := verifierFixture(t) var stdout bytes.Buffer err := run( []string{"-keys-only", "-trusted-keys", keySpec}, &stdout, ioDiscard{}, func(string) string { return "" }, ) require.NoError(t, err) assert.Contains(t, stdout.String(), "1 keys") } type ioDiscard struct{} func (ioDiscard) Write(data []byte) (int, error) { return len(data), nil }