mirror of
https://github.com/hanzoai/deploy.git
synced 2026-10-07 08:44:50 +00:00
go.mod still declared `module github.com/hanzoai/deploy` after the repo was renamed. A Go module path is not cosmetic — it is the resolvable location. So `go get github.com/hanzoai/cd` could not work, and anything importing this module had to spell a name that no longer exists at that address. The build log of v3.6.0 is where it surfaced: the compiler was still emitting github.com/hanzoai/deploy/cmd/cd/commands. 3541 import paths across 690 .go, 19 .proto, 4 .yaml, 3 .sh, 2 .mod, Procfile and Makefile. Plus the 62 ghcr.io/hanzoai/deploy image references, moved to ghcr.io/hanzoai/cd to match hanzo.yml's build target — a distinct string from the module path, replaced separately so neither could clobber the other. go mod tidy clean, CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build ./... clean. Co-authored-by: Hanzo Dev <[email protected]>
210 lines
5.4 KiB
Go
210 lines
5.4 KiB
Go
package e2e
|
|
|
|
import (
|
|
"encoding/json"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/spf13/cobra"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"google.golang.org/grpc/codes"
|
|
"google.golang.org/grpc/status"
|
|
|
|
"github.com/hanzoai/cd/cmd/cd/commands/headless"
|
|
"github.com/hanzoai/cd/pkg/apiclient/account"
|
|
"github.com/hanzoai/cd/pkg/apiclient/session"
|
|
. "github.com/hanzoai/cd/test/e2e/fixture"
|
|
accountFixture "github.com/hanzoai/cd/test/e2e/fixture/account"
|
|
"github.com/hanzoai/cd/util/errors"
|
|
utilio "github.com/hanzoai/cd/util/io"
|
|
jwtutil "github.com/hanzoai/cd/util/jwt"
|
|
)
|
|
|
|
func TestCreateAndUseAccount(t *testing.T) {
|
|
ctx := accountFixture.Given(t)
|
|
ctx.
|
|
Name("test").
|
|
When().
|
|
Create().
|
|
Then().
|
|
And(func(account *account.Account, _ error) {
|
|
assert.Equal(t, account.Name, ctx.GetName())
|
|
assert.Equal(t, []string{"login"}, account.Capabilities)
|
|
}).
|
|
When().
|
|
Login().
|
|
Then().
|
|
CurrentUser(func(user *session.GetUserInfoResponse, _ error) {
|
|
assert.True(t, user.LoggedIn)
|
|
assert.Equal(t, user.Username, ctx.GetName())
|
|
})
|
|
}
|
|
|
|
func TestCanIGetLogsAllow(t *testing.T) {
|
|
ctx := accountFixture.Given(t)
|
|
ctx.
|
|
Name("test").
|
|
Project(ProjectName).
|
|
When().
|
|
Create().
|
|
Login().
|
|
SetPermissions([]ACL{
|
|
{
|
|
Resource: "logs",
|
|
Action: "get",
|
|
Scope: ProjectName + "/*",
|
|
},
|
|
{
|
|
Resource: "apps",
|
|
Action: "get",
|
|
Scope: ProjectName + "/*",
|
|
},
|
|
}, "log-viewer").
|
|
CanIGetLogs().
|
|
Then().
|
|
AndCLIOutput(func(output string, _ error) {
|
|
assert.Contains(t, output, "yes")
|
|
})
|
|
}
|
|
|
|
func TestCanIGetLogsDeny(t *testing.T) {
|
|
ctx := accountFixture.Given(t)
|
|
ctx.
|
|
Name("test").
|
|
When().
|
|
Create().
|
|
Login().
|
|
CanIGetLogs().
|
|
Then().
|
|
AndCLIOutput(func(output string, _ error) {
|
|
assert.Contains(t, output, "no")
|
|
})
|
|
}
|
|
|
|
func TestCreateAndUseAccountCLI(t *testing.T) {
|
|
EnsureCleanState(t)
|
|
|
|
output, err := RunCli("account", "list")
|
|
errors.CheckError(err)
|
|
|
|
assert.Equal(t, `NAME ENABLED CAPABILITIES
|
|
admin true login`, output)
|
|
|
|
errors.CheckError(SetAccounts(map[string][]string{
|
|
"test": {"login", "apiKey"},
|
|
}))
|
|
|
|
output, err = RunCli("account", "list")
|
|
errors.CheckError(err)
|
|
|
|
assert.Equal(t, `NAME ENABLED CAPABILITIES
|
|
admin true login
|
|
test true login, apiKey`, output)
|
|
|
|
token, err := RunCli("account", "generate-token", "--account", "test")
|
|
errors.CheckError(err)
|
|
|
|
clientOpts := ArgoCDClientset.ClientOptions()
|
|
clientOpts.AuthToken = token
|
|
testAccountClientset := headless.NewClientOrDie(&clientOpts, &cobra.Command{})
|
|
|
|
closer, client := testAccountClientset.NewSessionClientOrDie()
|
|
defer utilio.Close(closer)
|
|
|
|
info, err := client.GetUserInfo(t.Context(), &session.GetUserInfoRequest{})
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, "test", info.Username)
|
|
}
|
|
|
|
func TestLoginBadCredentials(t *testing.T) {
|
|
EnsureCleanState(t)
|
|
|
|
closer, sessionClient := ArgoCDClientset.NewSessionClientOrDie()
|
|
defer utilio.Close(closer)
|
|
|
|
requests := []session.SessionCreateRequest{{
|
|
Username: "user-does-not-exist", Password: "some-password",
|
|
}, {
|
|
Username: "admin", Password: "bad-password",
|
|
}}
|
|
|
|
for _, r := range requests {
|
|
_, err := sessionClient.Create(t.Context(), &r)
|
|
require.Error(t, err)
|
|
errStatus, ok := status.FromError(err)
|
|
if !assert.True(t, ok) {
|
|
return
|
|
}
|
|
assert.Equal(t, codes.Unauthenticated, errStatus.Code())
|
|
assert.Equal(t, "Invalid username or password", errStatus.Message())
|
|
}
|
|
}
|
|
|
|
func TestAccountSessionToken(t *testing.T) {
|
|
// Create a unique temporary config directory for this test to support parallel execution
|
|
tmpDir := t.TempDir()
|
|
configPath := filepath.Join(tmpDir, "config")
|
|
|
|
ctx := accountFixture.Given(t)
|
|
ctx.
|
|
Name("test").
|
|
ConfigPath(configPath).
|
|
When().
|
|
Create().
|
|
CLILogin().
|
|
SessionToken().
|
|
Then().
|
|
AndCLIOutput(func(output string, err error) {
|
|
require.NoError(t, err, "session-token command should succeed")
|
|
|
|
// Verify token is not empty and is a valid JWT format
|
|
token := strings.TrimSpace(output)
|
|
assert.NotEmpty(t, token)
|
|
assert.True(t, jwtutil.IsValid(token), "Token should be a valid JWT format")
|
|
|
|
// Verify the token can be used for authentication
|
|
clientOpts := ArgoCDClientset.ClientOptions()
|
|
clientOpts.AuthToken = token
|
|
testAccountClientset := headless.NewClientOrDie(&clientOpts, &cobra.Command{})
|
|
|
|
closer, client := testAccountClientset.NewSessionClientOrDie()
|
|
defer utilio.Close(closer)
|
|
|
|
info, err := client.GetUserInfo(t.Context(), &session.GetUserInfoRequest{})
|
|
require.NoError(t, err)
|
|
|
|
// Verify the token belongs to the test user
|
|
assert.Equal(t, ctx.GetName(), info.Username)
|
|
assert.True(t, info.LoggedIn)
|
|
}).
|
|
When().
|
|
SessionTokenJSON().
|
|
Then().
|
|
AndCLIOutput(func(output string, err error) {
|
|
require.NoError(t, err, "session-token with JSON output should succeed")
|
|
|
|
// Parse JSON output
|
|
var tokenInfo map[string]any
|
|
err = json.Unmarshal([]byte(output), &tokenInfo)
|
|
require.NoError(t, err, "JSON output should be valid")
|
|
|
|
// Verify JSON fields exist
|
|
assert.Contains(t, tokenInfo, "token")
|
|
assert.Contains(t, tokenInfo, "type")
|
|
assert.Contains(t, tokenInfo, "issuer")
|
|
assert.Contains(t, tokenInfo, "username")
|
|
assert.Contains(t, tokenInfo, "has_refresh_token")
|
|
|
|
// Verify token type is local
|
|
assert.Equal(t, "local", tokenInfo["type"])
|
|
|
|
// Verify username
|
|
username, ok := tokenInfo["username"].(string)
|
|
require.True(t, ok, "username should be a string")
|
|
assert.Equal(t, ctx.GetName(), username)
|
|
})
|
|
}
|