action/command/root_test.go
Rama main · réplica de lectura del repositorio original.
package command
import (
"bytes"
"context"
"io"
"log/slog"
"net/http"
"net/http/httptest"
"testing"
"connectrpc.com/connect"
"github.com/stretchr/testify/require"
"github.com/bytebase/bytebase/action/world"
v1pb "github.com/bytebase/bytebase/backend/generated-go/v1"
"github.com/bytebase/bytebase/backend/generated-go/v1/v1connect"
)
func TestCheckVersionCompatibility(t *testing.T) {
t.Parallel()
tests := []struct {
name string
cliVersion string
serverVersion string
wantError string
}{
{
name: "same release version",
cliVersion: "3.14.0",
serverVersion: "3.14.0",
},
{
name: "release version within two minor versions",
cliVersion: "3.13.0",
serverVersion: "3.15.0",
},
{
name: "release version outside two minor versions",
cliVersion: "3.12.0",
serverVersion: "3.15.0",
wantError: "outside the compatibility window",
},
{
name: "newer release version is outside window",
cliVersion: "3.17.0",
serverVersion: "3.15.0",
wantError: "outside the compatibility window",
},
{
name: "release version with different major is outside window",
cliVersion: "2.15.0",
serverVersion: "3.15.0",
wantError: "outside the compatibility window",
},
{
name: "plain cloud action is not a dated cloud build",
cliVersion: "cloud-20260604",
serverVersion: "cloud",
wantError: "unable to parse",
},
{
name: "plain cloud server is not a dated cloud build",
cliVersion: "cloud",
serverVersion: "cloud-20260604",
wantError: "unable to parse",
},
{
name: "dated cloud builds match",
cliVersion: "cloud-20260604",
serverVersion: "cloud-20260604",
},
{
name: "dated cloud builds within seven days",
cliVersion: "cloud-20260604",
serverVersion: "cloud-20260611",
},
{
name: "newer dated cloud build is outside window",
cliVersion: "cloud-20260611",
serverVersion: "cloud-20260604",
wantError: "outside the compatibility window",
},
{
name: "dated cloud builds outside seven days",
cliVersion: "cloud-20260603",
serverVersion: "cloud-20260611",
wantError: "outside the compatibility window",
},
{
name: "cloud action is not compatible with release server",
cliVersion: "cloud-20260604",
serverVersion: "3.14.0",
wantError: "unable to compare",
},
{
name: "invalid release version is parse error",
cliVersion: "3",
serverVersion: "3.14.0",
wantError: "unable to parse",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
client := newActuatorInfoTestClient(t, tt.serverVersion)
w := &world.World{Logger: slog.New(slog.NewTextHandler(io.Discard, nil))}
err := checkVersionCompatibility(w, client, tt.cliVersion)
if tt.wantError == "" {
require.NoError(t, err)
return
}
require.Error(t, err)
require.Contains(t, err.Error(), tt.wantError)
})
}
}
func TestRootTargetsHelp(t *testing.T) {
t.Parallel()
var output bytes.Buffer
cmd := NewRootCommand(&world.World{})
cmd.SetArgs([]string{"--help"})
cmd.SetOut(&output)
cmd.SetErr(&output)
require.NoError(t, cmd.Execute())
require.Contains(t, output.String(), "workspace or project-instance databases")
require.Contains(t, output.String(), "single project database group")
}
func TestRecommendedActionTag(t *testing.T) {
t.Parallel()
releaseVersion, err := parseVersion("3.14.0")
require.NoError(t, err)
require.Equal(t, "3.14.0", recommendedActionTag(releaseVersion))
cloudVersion, err := parseVersion("cloud-20260604")
require.NoError(t, err)
require.Equal(t, "cloud", recommendedActionTag(cloudVersion))
}
func TestCheckVersionCompatibilityReturnsErrorOutsideWindow(t *testing.T) {
t.Parallel()
client := newActuatorInfoTestClient(t, "3.15.0")
w := &world.World{Logger: slog.New(slog.NewTextHandler(io.Discard, nil))}
err := checkVersionCompatibility(w, client, "3.12.0")
require.Error(t, err)
require.Contains(t, err.Error(), "outside the compatibility window")
require.Contains(t, err.Error(), "same major version")
require.Contains(t, err.Error(), "within 2 minor versions")
}
func TestCheckVersionCompatibilityReturnsUsefulCloudError(t *testing.T) {
t.Parallel()
client := newActuatorInfoTestClient(t, "cloud-20260604")
w := &world.World{Logger: slog.New(slog.NewTextHandler(io.Discard, nil))}
err := checkVersionCompatibility(w, client, "cloud-20260527")
require.Error(t, err)
require.Contains(t, err.Error(), "cloud-YYYYMMDD")
require.Contains(t, err.Error(), "within 7 days")
}
func TestCheckVersionCompatibilityReturnsParseError(t *testing.T) {
t.Parallel()
client := newActuatorInfoTestClient(t, "cloud")
w := &world.World{Logger: slog.New(slog.NewTextHandler(io.Discard, nil))}
err := checkVersionCompatibility(w, client, "cloud-20260604")
require.Error(t, err)
require.Contains(t, err.Error(), "unable to parse")
require.NotContains(t, err.Error(), "outside the compatibility window")
}
func TestCheckVersionCompatibilityAllowsVersionsWithinWindow(t *testing.T) {
t.Parallel()
client := newActuatorInfoTestClient(t, "cloud-20260604")
w := &world.World{Logger: slog.New(slog.NewTextHandler(io.Discard, nil))}
require.NoError(t, checkVersionCompatibility(w, client, "cloud-20260528"))
}
func newActuatorInfoTestClient(t *testing.T, version string) *client {
t.Helper()
path, handler := v1connect.NewActuatorServiceHandler(&actuatorInfoTestService{version: version})
mux := http.NewServeMux()
mux.Handle(path, handler)
server := httptest.NewServer(mux)
t.Cleanup(server.Close)
client, err := newClient(server.URL, "token", "", "", defaultClientOptions())
require.NoError(t, err)
t.Cleanup(client.close)
return client
}
type actuatorInfoTestService struct {
version string
}
func (s *actuatorInfoTestService) GetActuatorInfo(context.Context, *connect.Request[v1pb.GetActuatorInfoRequest]) (*connect.Response[v1pb.ActuatorInfo], error) {
return connect.NewResponse(&v1pb.ActuatorInfo{Version: s.version}), nil
}