Files
dingtalk-workspace-cli/test/scripts/verify_channels_test.go
T
修雨 3e9e76df2c feat: add stable and beta Homebrew channels (#613)
* feat: add stable and beta Homebrew channels

* fix: align beta formula with tap conventions

* fix: use dedicated token for Homebrew PRs

* chore: minimize release token permissions

* docs: record Homebrew token setup

* docs: keep Homebrew automation token long-lived

* fix(verify): assert channel versions and prove homebrew coexistence

The six-channel verifier previously ran `dws version` without comparing
it to the version each channel advertises, so a stale or wrong binary
still reported PASS. It also uninstalled stable before installing beta,
which could not prove the keg-only beta coexists with stable.

- smoke() now takes an expected version and fails the channel on mismatch
- npm/homebrew derive the expected version from the package manager;
  curl/upgrade derive it from the latest GitHub release tag
- homebrew installs keg-only beta while stable stays installed, then
  asserts stable's version, binary SHA and PATH link are unchanged
- cleanup uninstalls both script-installed formulae, still refusing to
  touch a pre-existing user install
- add regression tests for version assertion and coexistence semantics

* style(formula): satisfy brew style for stable and beta formulae

- reword desc so it no longer starts with the formula name
- drop the unnecessary `require "fileutils"` and use the mixed-in cp_r
  instead of the FileUtils. qualifier

* fix(verify): compare channel versions exactly

* fix(homebrew): keep generated formulae style-clean

* fix(homebrew): sync formulae with latest releases
2026-07-15 10:16:26 +08:00

160 lines
5.0 KiB
Go

// Copyright 2026 Alibaba Group
// Licensed under the Apache License, Version 2.0
package scripts
import (
"os"
"os/exec"
"path/filepath"
"strings"
"testing"
)
func readVerifier(t *testing.T) string {
t.Helper()
path := filepath.Join("..", "..", "verify", "verify-all-channels.sh")
data, err := os.ReadFile(path)
if err != nil {
t.Fatalf("read verifier: %v", err)
}
return string(data)
}
func TestVerifyAllChannelsScriptContract(t *testing.T) {
script := readVerifier(t)
for _, channel := range []string{
"curl", "powershell", "npm-stable", "npm-beta", "homebrew", "dws-upgrade",
} {
if !strings.Contains(script, channel) {
t.Errorf("verifier does not include channel %q", channel)
}
}
for _, check := range []string{" version", " --help", "npm uninstall", "brew uninstall"} {
if !strings.Contains(script, check) {
t.Errorf("verifier does not include lifecycle check %q", check)
}
}
path := filepath.Join("..", "..", "verify", "verify-all-channels.sh")
cmd := exec.Command("bash", "-n", path)
if output, err := cmd.CombinedOutput(); err != nil {
t.Fatalf("bash -n failed: %v\n%s", err, output)
}
}
func runVerifierSmoke(t *testing.T, reportedVersion, expectedVersion string) (string, error) {
t.Helper()
script := readVerifier(t)
start := strings.Index(script, "smoke() {")
if start == -1 {
t.Fatal("verifier does not define smoke()")
}
endMarker := "\n}\n\n# Latest stable"
end := strings.Index(script[start:], endMarker)
if end == -1 {
t.Fatal("could not isolate smoke() from verifier")
}
smokeFunction := script[start : start+end+2]
fakeBinary := filepath.Join(t.TempDir(), "dws")
fakeScript := `#!/bin/sh
case "${1:-}" in
version) printf '{"version":"v%s"}\n' "$FAKE_DWS_VERSION" ;;
--help) exit 0 ;;
*) exit 1 ;;
esac
`
if err := os.WriteFile(fakeBinary, []byte(fakeScript), 0o755); err != nil {
t.Fatalf("write fake dws: %v", err)
}
cmd := exec.Command("bash", "-c", smokeFunction+"\nsmoke \"$1\" \"$2\"", "smoke-test", fakeBinary, expectedVersion)
cmd.Env = append(os.Environ(), "FAKE_DWS_VERSION="+reportedVersion)
output, err := cmd.CombinedOutput()
return string(output), err
}
// TestVerifyAssertsExpectedVersion guards the requirement that a wrong or stale
// version fails the channel instead of silently reporting PASS.
func TestVerifyAssertsExpectedVersion(t *testing.T) {
tests := []struct {
name string
reportedVersion string
expectedVersion string
wantErr bool
wantMessage string
}{
{name: "stable exact match", reportedVersion: "1.0.51", expectedVersion: "1.0.51"},
{name: "beta exact match", reportedVersion: "1.0.52-beta.4", expectedVersion: "v1.0.52-beta.4"},
{
name: "stable rejects beta with shared prefix",
reportedVersion: "1.0.51-beta.1",
expectedVersion: "1.0.51",
wantErr: true,
wantMessage: "version mismatch",
},
{
name: "missing expected version fails closed",
reportedVersion: "1.0.51",
wantErr: true,
wantMessage: "expected version is empty",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
output, err := runVerifierSmoke(t, tt.reportedVersion, tt.expectedVersion)
if tt.wantErr && err == nil {
t.Fatalf("smoke unexpectedly passed:\n%s", output)
}
if !tt.wantErr && err != nil {
t.Fatalf("smoke failed: %v\n%s", err, output)
}
if tt.wantMessage != "" && !strings.Contains(output, tt.wantMessage) {
t.Fatalf("smoke output missing %q:\n%s", tt.wantMessage, output)
}
})
}
script := readVerifier(t)
// Homebrew and npm must derive the expected version from the package
// manager rather than trusting the binary's self-report unconditionally.
for _, source := range []string{"brew list --versions", "npm view"} {
if !strings.Contains(script, source) {
t.Errorf("verifier missing authoritative version source %q", source)
}
}
}
// TestVerifyHomebrewCoexistence guards the requirement that beta installs
// alongside stable (keg-only) without disturbing the stable channel.
func TestVerifyHomebrewCoexistence(t *testing.T) {
script := readVerifier(t)
stableInstall := `brew install "$TAP/$PACKAGE"`
betaInstall := `brew install "$TAP/$PACKAGE-beta"`
stableIdx := strings.Index(script, stableInstall)
betaIdx := strings.Index(script, betaInstall)
if stableIdx < 0 || betaIdx < 0 {
t.Fatalf("expected both stable and beta installs; stableIdx=%d betaIdx=%d", stableIdx, betaIdx)
}
if betaIdx <= stableIdx {
t.Errorf("beta must be installed after stable to prove coexistence")
}
// Stable must remain installed when beta lands: no uninstall between them.
between := script[stableIdx+len(stableInstall) : betaIdx]
if strings.Contains(between, "brew uninstall") {
t.Errorf("stable is uninstalled before beta install; cannot prove coexistence")
}
for _, marker := range []string{
"stable version changed after beta install",
"stable binary changed after beta install",
"stable link changed after beta install",
} {
if !strings.Contains(script, marker) {
t.Errorf("verifier missing coexistence assertion %q", marker)
}
}
}