Files
base/cmd/iam_user_test.go
zeekay 10ad25a966 cmd: add iam-user create subcommand for embedded mode
Operators booting Base with IAM_MODE=embedded need a way to seed users
without going through HTTP. This adds a base iam-user create <email>
subcommand that prompts for a password (stdin) or honors
IAM_USER_PASSWORD for CI, then bcrypts at cost 12 and persists to
_iam_users.

Exposes EnsureIAMUsersCollection / CreateEmbeddedIAMUser /
AuthenticateEmbeddedIAMUser as the package-level public surface so the
CLI doesn't have to reach into plugin internals.

Tests cover both the env-driven happy path and the empty-password
guard.
2026-05-15 09:55:57 -07:00

76 lines
2.1 KiB
Go

package cmd_test
import (
"bytes"
"strings"
"testing"
"github.com/hanzoai/base/cmd"
"github.com/hanzoai/base/plugins/platform"
"github.com/hanzoai/base/tests"
)
func TestIAMUserCreateCommand(t *testing.T) {
t.Setenv("IAM_MODE", "embedded")
t.Setenv("IAM_USER_PASSWORD", "test-password-123")
app, err := tests.NewTestApp()
if err != nil {
t.Fatalf("new app: %v", err)
}
defer app.Cleanup()
// Bring the _iam_users collection into existence the way the
// platform plugin would on first boot.
if err := platform.EnsureIAMUsersCollection(app); err != nil {
t.Fatalf("ensure collection: %v", err)
}
root := cmd.NewIAMUserCommand(app)
var out bytes.Buffer
root.SetOut(&out)
root.SetErr(&out)
root.SetArgs([]string{"create", "[email protected]", "--name", "Z"})
if err := root.Execute(); err != nil {
t.Fatalf("execute: %v; output=%s", err, out.String())
}
if got := out.String(); !strings.Contains(got, "created user") || !strings.Contains(got, "[email protected]") {
t.Errorf("unexpected output: %q", got)
}
// User should now authenticate.
if _, err := platform.AuthenticateEmbeddedIAMUser(app, "[email protected]", "test-password-123"); err != nil {
t.Fatalf("authenticate after create: %v", err)
}
}
func TestIAMUserCreateCommand_MissingPasswordEnv(t *testing.T) {
// No IAM_USER_PASSWORD and no stdin → should fail cleanly, not panic.
t.Setenv("IAM_USER_PASSWORD", "")
t.Setenv("IAM_MODE", "embedded")
app, err := tests.NewTestApp()
if err != nil {
t.Fatalf("new app: %v", err)
}
defer app.Cleanup()
if err := platform.EnsureIAMUsersCollection(app); err != nil {
t.Fatalf("ensure: %v", err)
}
root := cmd.NewIAMUserCommand(app)
root.SetIn(strings.NewReader("\n")) // empty password line
var out bytes.Buffer
root.SetOut(&out)
root.SetErr(&out)
root.SetArgs([]string{"create", "[email protected]"})
// We don't assert on the *exact* error string — we just want this
// branch to error rather than panic or silently create a user.
if err := root.Execute(); err == nil {
// If somehow it succeeded, the test must fail loudly.
t.Fatalf("expected create to fail with empty password, got success: %s", out.String())
}
}