Files
seaweedfs/weed/shell/command_s3_user_subcommands_test.go
T
Chris Lu 08dbfce493 shell: use tabwriter for aligned output, remove fragile string error checks
- Use text/tabwriter in runS3UserList and runS3UserAccessKeyList for
  properly aligned column output
- Remove fragile strings.Contains "not found" and "already exists"
  fallbacks in s3ShellIsNotFound and s3ShellIsAlreadyExists; rely on
  typed errors and gRPC status codes only
- Update test assertions to match tabwriter-formatted output
2026-04-07 11:24:42 -07:00

192 lines
6.0 KiB
Go

package shell
import (
"bytes"
"context"
"strings"
"testing"
"github.com/seaweedfs/seaweedfs/weed/credential/memory"
weediam "github.com/seaweedfs/seaweedfs/weed/iam"
"github.com/seaweedfs/seaweedfs/weed/pb/iam_pb"
"github.com/seaweedfs/seaweedfs/weed/s3api/policy_engine"
)
func newTestS3ShellStore(t *testing.T) *memory.MemoryStore {
t.Helper()
store := &memory.MemoryStore{}
if err := store.Initialize(nil, ""); err != nil {
t.Fatalf("initialize memory store: %v", err)
}
return store
}
func TestRunS3UserCreateGeneratesCredentials(t *testing.T) {
store := newTestS3ShellStore(t)
var out bytes.Buffer
err := runS3UserCreate(context.Background(), store, s3UserCreateOptions{
name: "alice",
generateCredentials: true,
email: "alice@example.com",
}, &out)
if err != nil {
t.Fatalf("runS3UserCreate: %v", err)
}
user, err := store.GetUser(context.Background(), "alice")
if err != nil {
t.Fatalf("GetUser: %v", err)
}
if len(user.Credentials) != 1 {
t.Fatalf("expected 1 credential, got %d", len(user.Credentials))
}
if got := user.Credentials[0].Status; got != weediam.AccessKeyStatusActive {
t.Fatalf("expected %q status, got %q", weediam.AccessKeyStatusActive, got)
}
if user.Account == nil || user.Account.EmailAddress != "alice@example.com" {
t.Fatalf("expected account email to be set, got %+v", user.Account)
}
output := out.String()
if !strings.Contains(output, `Created user "alice".`) {
t.Fatalf("expected created message, got %q", output)
}
if !strings.Contains(output, "Access Key:") || !strings.Contains(output, "Secret Key:") {
t.Fatalf("expected credential output, got %q", output)
}
}
func TestRunS3UserSetDisabledRejectsStaticUser(t *testing.T) {
store := newTestS3ShellStore(t)
if err := store.CreateUser(context.Background(), &iam_pb.Identity{
Name: "bootstrap-admin",
IsStatic: true,
}); err != nil {
t.Fatalf("CreateUser: %v", err)
}
err := runS3UserSetDisabled(context.Background(), store, "bootstrap-admin", true, &bytes.Buffer{})
if err == nil {
t.Fatal("expected static user mutation to fail")
}
if !strings.Contains(err.Error(), "-s3.config") {
t.Fatalf("expected static user guidance, got %v", err)
}
}
func TestRunS3UserListIncludesSourceAndStatus(t *testing.T) {
store := newTestS3ShellStore(t)
if err := store.CreateUser(context.Background(), &iam_pb.Identity{
Name: "alice",
Credentials: []*iam_pb.Credential{
{AccessKey: "ALICEKEY", SecretKey: "secret", Status: weediam.AccessKeyStatusActive},
},
}); err != nil {
t.Fatalf("CreateUser alice: %v", err)
}
if err := store.CreateUser(context.Background(), &iam_pb.Identity{
Name: "bootstrap-admin",
IsStatic: true,
Disabled: true,
}); err != nil {
t.Fatalf("CreateUser bootstrap-admin: %v", err)
}
var out bytes.Buffer
if err := runS3UserList(context.Background(), store, &out); err != nil {
t.Fatalf("runS3UserList: %v", err)
}
output := out.String()
if !strings.Contains(output, "NAME") || !strings.Contains(output, "SOURCE") || !strings.Contains(output, "STATUS") {
t.Fatalf("expected header, got %q", output)
}
if !strings.Contains(output, "alice") || !strings.Contains(output, "dynamic") || !strings.Contains(output, "enabled") {
t.Fatalf("expected dynamic user row, got %q", output)
}
if !strings.Contains(output, "bootstrap-admin") || !strings.Contains(output, "static") || !strings.Contains(output, "disabled") {
t.Fatalf("expected static user row, got %q", output)
}
}
func TestRunS3UserAccessKeyCreateGeneratesCredential(t *testing.T) {
store := newTestS3ShellStore(t)
if err := store.CreateUser(context.Background(), &iam_pb.Identity{Name: "alice"}); err != nil {
t.Fatalf("CreateUser: %v", err)
}
var out bytes.Buffer
err := runS3UserAccessKeyCreate(context.Background(), store, s3AccessKeyCreateOptions{
username: "alice",
generateCredentials: true,
}, &out)
if err != nil {
t.Fatalf("runS3UserAccessKeyCreate: %v", err)
}
user, err := store.GetUser(context.Background(), "alice")
if err != nil {
t.Fatalf("GetUser: %v", err)
}
if len(user.Credentials) != 1 {
t.Fatalf("expected 1 credential, got %d", len(user.Credentials))
}
if !strings.Contains(out.String(), `Created access key for user "alice".`) {
t.Fatalf("expected create message, got %q", out.String())
}
}
func TestRunS3PolicyAttachUpdatesUserPolicies(t *testing.T) {
store := newTestS3ShellStore(t)
if err := store.CreateUser(context.Background(), &iam_pb.Identity{Name: "alice"}); err != nil {
t.Fatalf("CreateUser: %v", err)
}
if err := store.CreatePolicy(context.Background(), "photos-rw", policy_engine.PolicyDocument{
Version: "2012-10-17",
}); err != nil {
t.Fatalf("CreatePolicy: %v", err)
}
var out bytes.Buffer
if err := runS3PolicyAttach(context.Background(), store, "alice", "photos-rw", &out); err != nil {
t.Fatalf("runS3PolicyAttach: %v", err)
}
user, err := store.GetUser(context.Background(), "alice")
if err != nil {
t.Fatalf("GetUser: %v", err)
}
if len(user.PolicyNames) != 1 || user.PolicyNames[0] != "photos-rw" {
t.Fatalf("expected attached policy, got %v", user.PolicyNames)
}
if !strings.Contains(out.String(), `Attached policy "photos-rw" to user "alice".`) {
t.Fatalf("expected attach message, got %q", out.String())
}
}
func TestRunS3PolicyDetachRejectsStaticUser(t *testing.T) {
store := newTestS3ShellStore(t)
if err := store.CreateUser(context.Background(), &iam_pb.Identity{
Name: "bootstrap-admin",
IsStatic: true,
PolicyNames: []string{"photos-rw"},
}); err != nil {
t.Fatalf("CreateUser: %v", err)
}
if err := store.CreatePolicy(context.Background(), "photos-rw", policy_engine.PolicyDocument{
Version: "2012-10-17",
}); err != nil {
t.Fatalf("CreatePolicy: %v", err)
}
err := runS3PolicyDetach(context.Background(), store, "bootstrap-admin", "photos-rw", &bytes.Buffer{})
if err == nil {
t.Fatal("expected static user mutation to fail")
}
if !strings.Contains(err.Error(), "-s3.config") {
t.Fatalf("expected static user guidance, got %v", err)
}
}