650 lines
19 KiB
Go
650 lines
19 KiB
Go
// Copyright 2026 Alibaba Group
|
|
// Licensed under the Apache License, Version 2.0
|
|
|
|
package upgrade
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
)
|
|
|
|
func TestFetchLatestRelease(t *testing.T) {
|
|
release := GitHubRelease{
|
|
TagName: "v1.0.6",
|
|
Name: "v1.0.6",
|
|
Body: "Bug fixes and improvements",
|
|
Prerelease: false,
|
|
Draft: false,
|
|
PublishedAt: "2026-04-01T09:29:05Z",
|
|
Assets: []GitHubAsset{
|
|
{Name: "dws-darwin-arm64.tar.gz", Size: 3942308, BrowserDownloadURL: "https://example.com/dws-darwin-arm64.tar.gz"},
|
|
{Name: "dws-skills.zip", Size: 50000, BrowserDownloadURL: "https://example.com/dws-skills.zip"},
|
|
{Name: "checksums.txt", Size: 534, BrowserDownloadURL: "https://example.com/checksums.txt"},
|
|
},
|
|
}
|
|
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.URL.Path != "/repos/DingTalk-Real-AI/dingtalk-workspace-cli/releases/latest" {
|
|
t.Errorf("unexpected path: %s", r.URL.Path)
|
|
http.NotFound(w, r)
|
|
return
|
|
}
|
|
w.Header().Set("Content-Type", "application/json")
|
|
json.NewEncoder(w).Encode(release)
|
|
}))
|
|
defer server.Close()
|
|
|
|
client := NewClientWithBaseURL(server.URL)
|
|
info, err := client.FetchLatestRelease()
|
|
if err != nil {
|
|
t.Fatalf("FetchLatestRelease() error = %v", err)
|
|
}
|
|
|
|
if info.Version != "1.0.6" {
|
|
t.Errorf("Version = %q, want %q", info.Version, "1.0.6")
|
|
}
|
|
if info.Date != "2026-04-01" {
|
|
t.Errorf("Date = %q, want %q", info.Date, "2026-04-01")
|
|
}
|
|
if len(info.Assets) != 3 {
|
|
t.Errorf("len(Assets) = %d, want 3", len(info.Assets))
|
|
}
|
|
}
|
|
|
|
func TestFetchReleaseByTag(t *testing.T) {
|
|
release := GitHubRelease{
|
|
TagName: "v1.0.5",
|
|
PublishedAt: "2026-03-25T10:00:00Z",
|
|
Assets: []GitHubAsset{
|
|
{Name: "dws-darwin-arm64.tar.gz", Size: 3800000, BrowserDownloadURL: "https://example.com/dws-darwin-arm64.tar.gz"},
|
|
},
|
|
}
|
|
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
expected := "/repos/DingTalk-Real-AI/dingtalk-workspace-cli/releases/tags/v1.0.5"
|
|
if r.URL.Path != expected {
|
|
t.Errorf("path = %q, want %q", r.URL.Path, expected)
|
|
http.NotFound(w, r)
|
|
return
|
|
}
|
|
json.NewEncoder(w).Encode(release)
|
|
}))
|
|
defer server.Close()
|
|
|
|
client := NewClientWithBaseURL(server.URL)
|
|
|
|
// Without "v" prefix
|
|
info, err := client.FetchReleaseByTag("1.0.5")
|
|
if err != nil {
|
|
t.Fatalf("FetchReleaseByTag() error = %v", err)
|
|
}
|
|
if info.Version != "1.0.5" {
|
|
t.Errorf("Version = %q, want %q", info.Version, "1.0.5")
|
|
}
|
|
}
|
|
|
|
func TestFetchAllReleases(t *testing.T) {
|
|
releases := []GitHubRelease{
|
|
{TagName: "v1.0.6", PublishedAt: "2026-04-01T09:00:00Z", Draft: false, Prerelease: false},
|
|
{TagName: "v1.0.6-beta", PublishedAt: "2026-03-28T09:00:00Z", Draft: false, Prerelease: true},
|
|
{TagName: "v1.0.5", PublishedAt: "2026-03-25T09:00:00Z", Draft: false, Prerelease: false},
|
|
{TagName: "v1.0.4-draft", PublishedAt: "2026-03-20T09:00:00Z", Draft: true, Prerelease: false},
|
|
}
|
|
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
json.NewEncoder(w).Encode(releases)
|
|
}))
|
|
defer server.Close()
|
|
|
|
client := NewClientWithBaseURL(server.URL)
|
|
versions, err := client.FetchAllReleases()
|
|
if err != nil {
|
|
t.Fatalf("FetchAllReleases() error = %v", err)
|
|
}
|
|
|
|
// Draft should be filtered out
|
|
if len(versions) != 3 {
|
|
t.Fatalf("len(versions) = %d, want 3", len(versions))
|
|
}
|
|
if versions[0].Version != "1.0.6" {
|
|
t.Errorf("versions[0].Version = %q, want %q", versions[0].Version, "1.0.6")
|
|
}
|
|
if versions[1].Prerelease != true {
|
|
t.Errorf("versions[1].Prerelease = false, want true")
|
|
}
|
|
}
|
|
|
|
func TestFetchReleaseVersions_ByTrack(t *testing.T) {
|
|
releases := []GitHubRelease{
|
|
{TagName: "delivery/remove-discovery-yolo-latest", PublishedAt: "2026-04-04T09:00:00Z", Draft: false, Prerelease: true},
|
|
{TagName: "v1.0.7-beta.2", PublishedAt: "2026-04-03T09:00:00Z", Draft: false, Prerelease: true},
|
|
{TagName: "feat/remove-discovery-v1", PublishedAt: "2026-04-02T09:00:00Z", Draft: false, Prerelease: false},
|
|
{TagName: "v1.0.6", PublishedAt: "2026-04-01T09:00:00Z", Draft: false, Prerelease: false},
|
|
{TagName: "v1.0.7-beta.1", PublishedAt: "2026-03-28T09:00:00Z", Draft: false, Prerelease: true},
|
|
{TagName: "v1.0.5", PublishedAt: "2026-03-25T09:00:00Z", Draft: false, Prerelease: false},
|
|
{TagName: "v1.0.8-beta-draft", PublishedAt: "2026-04-04T09:00:00Z", Draft: true, Prerelease: true},
|
|
}
|
|
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
json.NewEncoder(w).Encode(releases)
|
|
}))
|
|
defer server.Close()
|
|
|
|
client := NewClientWithBaseURL(server.URL)
|
|
|
|
stable, err := client.FetchReleaseVersions(ReleaseTrackRelease)
|
|
if err != nil {
|
|
t.Fatalf("FetchReleaseVersions(release) error = %v", err)
|
|
}
|
|
if len(stable) != 2 || stable[0].Version != "1.0.6" || stable[1].Version != "1.0.5" {
|
|
t.Fatalf("stable versions = %#v, want 1.0.6 and 1.0.5", stable)
|
|
}
|
|
|
|
beta, err := client.FetchReleaseVersions(ReleaseTrackBeta)
|
|
if err != nil {
|
|
t.Fatalf("FetchReleaseVersions(beta) error = %v", err)
|
|
}
|
|
if len(beta) != 2 || beta[0].Version != "1.0.7-beta.2" || beta[1].Version != "1.0.7-beta.1" {
|
|
t.Fatalf("beta versions = %#v, want beta.2 and beta.1", beta)
|
|
}
|
|
}
|
|
|
|
func TestFetchLatestStableRelease(t *testing.T) {
|
|
releases := []GitHubRelease{
|
|
{TagName: "feat/remove-discovery-v1", PublishedAt: "2026-04-02T09:00:00Z", Draft: false, Prerelease: false},
|
|
{TagName: "v1.0.7-beta.1", PublishedAt: "2026-04-01T09:00:00Z", Draft: false, Prerelease: true},
|
|
{TagName: "v1.0.6", PublishedAt: "2026-03-30T09:00:00Z", Draft: false, Prerelease: false},
|
|
}
|
|
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
expected := "/repos/DingTalk-Real-AI/dingtalk-workspace-cli/releases"
|
|
if r.URL.Path != expected {
|
|
t.Errorf("path = %q, want %q", r.URL.Path, expected)
|
|
}
|
|
json.NewEncoder(w).Encode(releases)
|
|
}))
|
|
defer server.Close()
|
|
|
|
client := NewClientWithBaseURL(server.URL)
|
|
info, err := client.FetchLatestStableRelease()
|
|
if err != nil {
|
|
t.Fatalf("FetchLatestStableRelease() error = %v", err)
|
|
}
|
|
if info.Version != "1.0.6" {
|
|
t.Errorf("Version = %q, want 1.0.6", info.Version)
|
|
}
|
|
if info.Prerelease {
|
|
t.Error("Prerelease = true, want false")
|
|
}
|
|
}
|
|
|
|
func TestFetchLatestPrerelease(t *testing.T) {
|
|
releases := []GitHubRelease{
|
|
{TagName: "delivery/remove-discovery-yolo-latest", PublishedAt: "2026-04-02T09:00:00Z", Draft: false, Prerelease: true},
|
|
{TagName: "v1.0.6", PublishedAt: "2026-04-01T09:00:00Z", Draft: false, Prerelease: false},
|
|
{TagName: "v1.0.7-beta.2", PublishedAt: "2026-03-30T09:00:00Z", Draft: false, Prerelease: true},
|
|
{TagName: "v1.0.7-beta.1", PublishedAt: "2026-03-28T09:00:00Z", Draft: false, Prerelease: true},
|
|
}
|
|
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
expected := "/repos/DingTalk-Real-AI/dingtalk-workspace-cli/releases"
|
|
if r.URL.Path != expected {
|
|
t.Errorf("path = %q, want %q", r.URL.Path, expected)
|
|
}
|
|
json.NewEncoder(w).Encode(releases)
|
|
}))
|
|
defer server.Close()
|
|
|
|
client := NewClientWithBaseURL(server.URL)
|
|
info, err := client.FetchLatestPrerelease()
|
|
if err != nil {
|
|
t.Fatalf("FetchLatestPrerelease() error = %v", err)
|
|
}
|
|
if info.Version != "1.0.7-beta.2" {
|
|
t.Errorf("Version = %q, want 1.0.7-beta.2", info.Version)
|
|
}
|
|
if !info.Prerelease {
|
|
t.Error("Prerelease = false, want true")
|
|
}
|
|
}
|
|
|
|
func TestFetchLatestPrerelease_NotFound(t *testing.T) {
|
|
releases := []GitHubRelease{
|
|
{TagName: "v1.0.6", Draft: false, Prerelease: false},
|
|
{TagName: "v1.0.7-beta.1", Draft: true, Prerelease: true},
|
|
}
|
|
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
json.NewEncoder(w).Encode(releases)
|
|
}))
|
|
defer server.Close()
|
|
|
|
client := NewClientWithBaseURL(server.URL)
|
|
if _, err := client.FetchLatestPrerelease(); err == nil {
|
|
t.Fatal("FetchLatestPrerelease() error = nil, want not found")
|
|
}
|
|
}
|
|
|
|
func TestFindBinaryAsset(t *testing.T) {
|
|
assets := []GitHubAsset{
|
|
{Name: "checksums.txt", Size: 534},
|
|
{Name: "dws-darwin-amd64.tar.gz", Size: 4276643},
|
|
{Name: "dws-darwin-arm64.tar.gz", Size: 3942308},
|
|
{Name: "dws-linux-amd64.tar.gz", Size: 4394696},
|
|
{Name: "dws-windows-amd64.zip", Size: 4500000},
|
|
{Name: "dws-skills.zip", Size: 50000},
|
|
}
|
|
|
|
tests := []struct {
|
|
goos, goarch string
|
|
wantName string
|
|
wantErr bool
|
|
}{
|
|
{"darwin", "arm64", "dws-darwin-arm64.tar.gz", false},
|
|
{"darwin", "amd64", "dws-darwin-amd64.tar.gz", false},
|
|
{"linux", "amd64", "dws-linux-amd64.tar.gz", false},
|
|
{"windows", "amd64", "dws-windows-amd64.zip", false},
|
|
{"freebsd", "amd64", "", true},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
asset, err := FindBinaryAssetFor(assets, tt.goos, tt.goarch)
|
|
if tt.wantErr {
|
|
if err == nil {
|
|
t.Errorf("FindBinaryAssetFor(%s, %s) expected error", tt.goos, tt.goarch)
|
|
}
|
|
continue
|
|
}
|
|
if err != nil {
|
|
t.Errorf("FindBinaryAssetFor(%s, %s) error = %v", tt.goos, tt.goarch, err)
|
|
continue
|
|
}
|
|
if asset.Name != tt.wantName {
|
|
t.Errorf("FindBinaryAssetFor(%s, %s).Name = %q, want %q", tt.goos, tt.goarch, asset.Name, tt.wantName)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestFindSkillsAsset(t *testing.T) {
|
|
assets := []GitHubAsset{
|
|
{Name: "dws-darwin-arm64.tar.gz"},
|
|
{Name: "dws-skills.zip", Size: 50000},
|
|
{Name: "checksums.txt"},
|
|
}
|
|
|
|
skill := FindSkillsAsset(assets)
|
|
if skill == nil {
|
|
t.Fatal("FindSkillsAsset() returned nil")
|
|
}
|
|
if skill.Name != "dws-skills.zip" {
|
|
t.Errorf("Name = %q, want %q", skill.Name, "dws-skills.zip")
|
|
}
|
|
|
|
// No skills asset
|
|
noSkills := []GitHubAsset{{Name: "dws-darwin-arm64.tar.gz"}}
|
|
if got := FindSkillsAsset(noSkills); got != nil {
|
|
t.Errorf("FindSkillsAsset() = %v, want nil", got)
|
|
}
|
|
}
|
|
|
|
func TestFindChecksumsAsset(t *testing.T) {
|
|
assets := []GitHubAsset{
|
|
{Name: "dws-darwin-arm64.tar.gz"},
|
|
{Name: "checksums.txt", Size: 534},
|
|
}
|
|
|
|
checksums := FindChecksumsAsset(assets)
|
|
if checksums == nil {
|
|
t.Fatal("FindChecksumsAsset() returned nil")
|
|
}
|
|
if checksums.Name != "checksums.txt" {
|
|
t.Errorf("Name = %q, want %q", checksums.Name, "checksums.txt")
|
|
}
|
|
}
|
|
|
|
func TestExtractDigestSHA256(t *testing.T) {
|
|
tests := []struct {
|
|
digest string
|
|
want string
|
|
}{
|
|
{"sha256:abcdef1234567890", "abcdef1234567890"},
|
|
{"sha256:", ""},
|
|
{"md5:abcdef", ""},
|
|
{"", ""},
|
|
}
|
|
for _, tt := range tests {
|
|
got := ExtractDigestSHA256(tt.digest)
|
|
if got != tt.want {
|
|
t.Errorf("ExtractDigestSHA256(%q) = %q, want %q", tt.digest, got, tt.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestRateLimitError(t *testing.T) {
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("X-RateLimit-Remaining", "0")
|
|
w.WriteHeader(http.StatusForbidden)
|
|
}))
|
|
defer server.Close()
|
|
|
|
client := NewClientWithBaseURL(server.URL)
|
|
_, err := client.FetchLatestRelease()
|
|
if err == nil {
|
|
t.Fatal("expected rate limit error")
|
|
}
|
|
if got := err.Error(); !contains(got, "频率超限") {
|
|
t.Errorf("error = %q, want to contain rate limit message", got)
|
|
}
|
|
}
|
|
|
|
func TestFetchLatestRelease_NotFound(t *testing.T) {
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusNotFound)
|
|
}))
|
|
defer server.Close()
|
|
|
|
client := NewClientWithBaseURL(server.URL)
|
|
_, err := client.FetchLatestRelease()
|
|
if err == nil {
|
|
t.Fatal("expected error for 404")
|
|
}
|
|
if !contains(err.Error(), "404") {
|
|
t.Errorf("error = %q, want to contain '404'", err.Error())
|
|
}
|
|
}
|
|
|
|
func TestFetchReleaseByTag_InvalidJSON(t *testing.T) {
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.Write([]byte("{invalid json"))
|
|
}))
|
|
defer server.Close()
|
|
|
|
client := NewClientWithBaseURL(server.URL)
|
|
_, err := client.FetchReleaseByTag("v1.0.0")
|
|
if err == nil {
|
|
t.Fatal("expected error for invalid JSON")
|
|
}
|
|
}
|
|
|
|
func TestFetchLatestRelease_FieldMapping(t *testing.T) {
|
|
release := GitHubRelease{
|
|
TagName: "v2.1.0",
|
|
Body: "## Changelog\n* abc1234 - add new feature\n* def5678 Merge branch 'main'",
|
|
Prerelease: true,
|
|
PublishedAt: "2026-06-15T14:30:00Z",
|
|
HTMLURL: "https://github.com/DingTalk-Real-AI/dingtalk-workspace-cli/releases/tag/v2.1.0",
|
|
Assets: []GitHubAsset{},
|
|
}
|
|
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
json.NewEncoder(w).Encode(release)
|
|
}))
|
|
defer server.Close()
|
|
|
|
client := NewClientWithBaseURL(server.URL)
|
|
info, err := client.FetchLatestRelease()
|
|
if err != nil {
|
|
t.Fatalf("error = %v", err)
|
|
}
|
|
|
|
if info.Version != "2.1.0" {
|
|
t.Errorf("Version = %q, want %q", info.Version, "2.1.0")
|
|
}
|
|
if info.Date != "2026-06-15" {
|
|
t.Errorf("Date = %q, want %q", info.Date, "2026-06-15")
|
|
}
|
|
if !info.Prerelease {
|
|
t.Error("Prerelease = false, want true")
|
|
}
|
|
if info.HTMLURL != release.HTMLURL {
|
|
t.Errorf("HTMLURL = %q, want %q", info.HTMLURL, release.HTMLURL)
|
|
}
|
|
if info.Changelog == "" {
|
|
t.Error("Changelog should not be empty")
|
|
}
|
|
}
|
|
|
|
func TestFetchAllReleases_EmptyList(t *testing.T) {
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
json.NewEncoder(w).Encode([]GitHubRelease{})
|
|
}))
|
|
defer server.Close()
|
|
|
|
client := NewClientWithBaseURL(server.URL)
|
|
versions, err := client.FetchAllReleases()
|
|
if err != nil {
|
|
t.Fatalf("error = %v", err)
|
|
}
|
|
if len(versions) != 0 {
|
|
t.Errorf("len = %d, want 0", len(versions))
|
|
}
|
|
}
|
|
|
|
func TestFetchAllReleases_AllDrafts(t *testing.T) {
|
|
releases := []GitHubRelease{
|
|
{TagName: "v1.0.0", Draft: true},
|
|
{TagName: "v0.9.0", Draft: true},
|
|
}
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
json.NewEncoder(w).Encode(releases)
|
|
}))
|
|
defer server.Close()
|
|
|
|
client := NewClientWithBaseURL(server.URL)
|
|
versions, err := client.FetchAllReleases()
|
|
if err != nil {
|
|
t.Fatalf("error = %v", err)
|
|
}
|
|
if len(versions) != 0 {
|
|
t.Errorf("all-drafts: len = %d, want 0", len(versions))
|
|
}
|
|
}
|
|
|
|
func TestFetchReleaseByTag_WithVPrefix(t *testing.T) {
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
expected := "/repos/DingTalk-Real-AI/dingtalk-workspace-cli/releases/tags/v1.0.5"
|
|
if r.URL.Path != expected {
|
|
t.Errorf("path = %q, want %q", r.URL.Path, expected)
|
|
}
|
|
json.NewEncoder(w).Encode(GitHubRelease{TagName: "v1.0.5", PublishedAt: "2026-01-01T00:00:00Z"})
|
|
}))
|
|
defer server.Close()
|
|
|
|
client := NewClientWithBaseURL(server.URL)
|
|
info, err := client.FetchReleaseByTag("v1.0.5") // already has "v"
|
|
if err != nil {
|
|
t.Fatalf("error = %v", err)
|
|
}
|
|
if info.Version != "1.0.5" {
|
|
t.Errorf("Version = %q, want %q", info.Version, "1.0.5")
|
|
}
|
|
}
|
|
|
|
func TestFindChecksumsAsset_NotFound(t *testing.T) {
|
|
assets := []GitHubAsset{{Name: "dws-darwin-arm64.tar.gz"}}
|
|
if got := FindChecksumsAsset(assets); got != nil {
|
|
t.Errorf("FindChecksumsAsset() = %v, want nil", got)
|
|
}
|
|
}
|
|
|
|
func TestFindBinaryAssetFor_EmptyAssets(t *testing.T) {
|
|
_, err := FindBinaryAssetFor(nil, "darwin", "arm64")
|
|
if err == nil {
|
|
t.Error("expected error for empty assets")
|
|
}
|
|
}
|
|
|
|
func TestStripV(t *testing.T) {
|
|
tests := []struct{ in, want string }{
|
|
{"v1.0.0", "1.0.0"},
|
|
{"1.0.0", "1.0.0"},
|
|
{"v", ""},
|
|
{"", ""},
|
|
}
|
|
for _, tt := range tests {
|
|
got := stripV(tt.in)
|
|
if got != tt.want {
|
|
t.Errorf("stripV(%q) = %q, want %q", tt.in, got, tt.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestFormatDate(t *testing.T) {
|
|
tests := []struct{ in, want string }{
|
|
{"2026-04-01T09:29:05Z", "2026-04-01"},
|
|
{"invalid-date", "invalid-date"},
|
|
{"", ""},
|
|
}
|
|
for _, tt := range tests {
|
|
got := formatDate(tt.in)
|
|
if got != tt.want {
|
|
t.Errorf("formatDate(%q) = %q, want %q", tt.in, got, tt.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestTruncateBody(t *testing.T) {
|
|
tests := []struct {
|
|
body string
|
|
maxLen int
|
|
want string
|
|
}{
|
|
{"short", 100, "short"},
|
|
{"line1\nline2", 100, "line1"},
|
|
{"a very long string here", 10, "a very ..."},
|
|
{"", 10, ""},
|
|
}
|
|
for _, tt := range tests {
|
|
got := truncateBody(tt.body, tt.maxLen)
|
|
if got != tt.want {
|
|
t.Errorf("truncateBody(%q, %d) = %q, want %q", tt.body, tt.maxLen, got, tt.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestGithubToken(t *testing.T) {
|
|
// Clean env first
|
|
t.Setenv("GITHUB_TOKEN", "")
|
|
t.Setenv("GH_TOKEN", "")
|
|
if got := githubToken(); got != "" {
|
|
t.Errorf("expected empty token, got %q", got)
|
|
}
|
|
|
|
t.Setenv("GITHUB_TOKEN", "gh-tok-123")
|
|
if got := githubToken(); got != "gh-tok-123" {
|
|
t.Errorf("expected gh-tok-123, got %q", got)
|
|
}
|
|
|
|
t.Setenv("GITHUB_TOKEN", "")
|
|
t.Setenv("GH_TOKEN", "gh-alt-456")
|
|
if got := githubToken(); got != "gh-alt-456" {
|
|
t.Errorf("expected gh-alt-456, got %q", got)
|
|
}
|
|
}
|
|
|
|
func TestRepositoryFromEnv(t *testing.T) {
|
|
t.Setenv("DWS_UPGRADE_REPOSITORY", "")
|
|
owner, repo, err := repositoryFromEnv()
|
|
if err != nil {
|
|
t.Fatalf("repositoryFromEnv default error = %v", err)
|
|
}
|
|
if owner != defaultOwner || repo != defaultRepo {
|
|
t.Fatalf("repositoryFromEnv default = %s/%s, want %s/%s", owner, repo, defaultOwner, defaultRepo)
|
|
}
|
|
|
|
t.Setenv("DWS_UPGRADE_REPOSITORY", "PeterGuy326/dingtalk-workspace-cli")
|
|
owner, repo, err = repositoryFromEnv()
|
|
if err != nil {
|
|
t.Fatalf("repositoryFromEnv owner/repo error = %v", err)
|
|
}
|
|
if owner != "PeterGuy326" || repo != "dingtalk-workspace-cli" {
|
|
t.Fatalf("repositoryFromEnv owner/repo = %s/%s", owner, repo)
|
|
}
|
|
|
|
t.Setenv("DWS_UPGRADE_REPOSITORY", "https://github.com/PeterGuy326/dingtalk-workspace-cli.git")
|
|
owner, repo, err = repositoryFromEnv()
|
|
if err != nil {
|
|
t.Fatalf("repositoryFromEnv URL error = %v", err)
|
|
}
|
|
if owner != "PeterGuy326" || repo != "dingtalk-workspace-cli" {
|
|
t.Fatalf("repositoryFromEnv URL = %s/%s", owner, repo)
|
|
}
|
|
}
|
|
|
|
func TestRepositoryFromEnv_Invalid(t *testing.T) {
|
|
t.Setenv("DWS_UPGRADE_REPOSITORY", "not-a-repository")
|
|
client := NewClient()
|
|
if _, err := client.FetchLatestRelease(); err == nil {
|
|
t.Fatal("FetchLatestRelease with invalid DWS_UPGRADE_REPOSITORY error = nil")
|
|
}
|
|
}
|
|
|
|
func TestIsVersionLikeTag(t *testing.T) {
|
|
tests := []struct {
|
|
tag string
|
|
want bool
|
|
}{
|
|
{"v1.0.7", true},
|
|
{"1.0.7", true},
|
|
{"v1.0.7-beta.1", true},
|
|
{"v1.0.50-yolo-preview", true},
|
|
{"delivery/remove-discovery-yolo-latest", false},
|
|
{"feat/remove-discovery-v1", false},
|
|
{"v1.0", false},
|
|
}
|
|
for _, tt := range tests {
|
|
if got := isVersionLikeTag(tt.tag); got != tt.want {
|
|
t.Errorf("isVersionLikeTag(%q) = %v, want %v", tt.tag, got, tt.want)
|
|
}
|
|
}
|
|
|
|
if !isStableVersionTag("v1.0.7") {
|
|
t.Error("isStableVersionTag(v1.0.7) = false, want true")
|
|
}
|
|
if isStableVersionTag("v1.0.7-beta.1") {
|
|
t.Error("isStableVersionTag(v1.0.7-beta.1) = true, want false")
|
|
}
|
|
if !isPrereleaseVersionTag("v1.0.7-beta.1") {
|
|
t.Error("isPrereleaseVersionTag(v1.0.7-beta.1) = false, want true")
|
|
}
|
|
if isPrereleaseVersionTag("v1.0.7") {
|
|
t.Error("isPrereleaseVersionTag(v1.0.7) = true, want false")
|
|
}
|
|
}
|
|
|
|
func TestGetJSON_SetsHeaders(t *testing.T) {
|
|
t.Setenv("GITHUB_TOKEN", "test-token-abc")
|
|
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if ua := r.Header.Get("User-Agent"); ua != userAgent {
|
|
t.Errorf("User-Agent = %q, want %q", ua, userAgent)
|
|
}
|
|
if accept := r.Header.Get("Accept"); accept != "application/vnd.github+json" {
|
|
t.Errorf("Accept = %q, want application/vnd.github+json", accept)
|
|
}
|
|
if auth := r.Header.Get("Authorization"); auth != "token test-token-abc" {
|
|
t.Errorf("Authorization = %q, want %q", auth, "token test-token-abc")
|
|
}
|
|
json.NewEncoder(w).Encode(GitHubRelease{TagName: "v1.0.0", PublishedAt: "2026-01-01T00:00:00Z"})
|
|
}))
|
|
defer server.Close()
|
|
|
|
client := NewClientWithBaseURL(server.URL)
|
|
_, err := client.FetchLatestRelease()
|
|
if err != nil {
|
|
t.Fatalf("error = %v", err)
|
|
}
|
|
}
|
|
|
|
func contains(s, substr string) bool {
|
|
for i := 0; i <= len(s)-len(substr); i++ {
|
|
if s[i:i+len(substr)] == substr {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|