package zitadel_test import ( "context" "encoding/json" "io" "net/http" "net/http/httptest" "net/url" "testing" "gateway/internal/library/zitadel" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestCreateHumanUser(t *testing.T) { t.Parallel() var gotAuth string var gotBody map[string]any srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { switch { case r.Method == http.MethodPost && r.URL.Path == "/v2/users/human": gotAuth = r.Header.Get("Authorization") assert.NoError(t, json.NewDecoder(r.Body).Decode(&gotBody)) w.Header().Set("Content-Type", "application/json") _, _ = w.Write([]byte(`{"userId":"zit-123"}`)) default: http.NotFound(w, r) } })) t.Cleanup(srv.Close) c, err := zitadel.NewClient(zitadel.Conf{ Issuer: srv.URL, ServiceUserToken: "pat-test", DefaultOrgID: "org-1", }) require.NoError(t, err) res, err := c.CreateHumanUser(context.Background(), zitadel.CreateHumanUserRequest{ Email: "alice@example.com", Password: "Secret123!", DisplayName: "Alice Smith", Language: "zh-tw", }) require.NoError(t, err) require.Equal(t, "zit-123", res.UserID) require.Equal(t, "Bearer pat-test", gotAuth) require.Equal(t, "alice@example.com", gotBody["username"]) require.Equal(t, "org-1", gotBody["organizationId"]) } func TestCreateHumanUserConflict(t *testing.T) { t.Parallel() srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { http.Error(w, "exists", http.StatusConflict) })) t.Cleanup(srv.Close) c, err := zitadel.NewClient(zitadel.Conf{Issuer: srv.URL, ServiceUserToken: testPAT}) require.NoError(t, err) _, err = c.CreateHumanUser(context.Background(), zitadel.CreateHumanUserRequest{ Email: "dup@example.com", Password: "x", }) require.ErrorIs(t, err, zitadel.ErrUserAlreadyExists) } func TestDeactivateUser(t *testing.T) { t.Parallel() srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { assert.Equal(t, http.MethodPost, r.Method) assert.Equal(t, "/v2/users/u-99/deactivate", r.URL.Path) w.WriteHeader(http.StatusOK) _, _ = w.Write([]byte(`{"details":{}}`)) })) t.Cleanup(srv.Close) c, err := zitadel.NewClient(zitadel.Conf{Issuer: srv.URL, ServiceUserToken: testPAT}) require.NoError(t, err) require.NoError(t, c.DeactivateUser(context.Background(), "u-99")) } func TestVerifyPassword(t *testing.T) { t.Parallel() srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { assert.Equal(t, "/oauth/v2/token", r.URL.Path) body, _ := io.ReadAll(r.Body) vals := parseForm(string(body)) assert.Equal(t, "password", vals["grant_type"]) assert.Equal(t, testClientID, vals["client_id"]) assert.Equal(t, "alice@example.com", vals["username"]) if vals["password"] != "ok" { http.Error(w, "invalid", http.StatusUnauthorized) return } w.Header().Set("Content-Type", "application/json") _, _ = w.Write([]byte(`{"access_token":"at","id_token":"id","expires_in":3600,"token_type":"Bearer"}`)) })) t.Cleanup(srv.Close) c, err := zitadel.NewClient(zitadel.Conf{ Issuer: srv.URL, ServiceUserToken: testPAT, OAuthClientID: testClientID, OAuthClientSecret: testSecret, }) require.NoError(t, err) tok, err := c.VerifyPassword(context.Background(), "alice@example.com", "ok") require.NoError(t, err) require.Equal(t, "at", tok.AccessToken) require.Equal(t, "id", tok.IDToken) _, err = c.VerifyPassword(context.Background(), "alice@example.com", "bad") require.ErrorIs(t, err, zitadel.ErrInvalidCredentials) } func TestVerifyPasswordSession(t *testing.T) { t.Parallel() srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") switch { case r.Method == http.MethodPost && r.URL.Path == "/v2/sessions": _, _ = w.Write([]byte(`{"sessionId":"sess-1"}`)) case r.Method == http.MethodPatch && r.URL.Path == "/v2/sessions/sess-1": _, _ = w.Write([]byte(`{"sessionToken":"tok"}`)) case r.Method == http.MethodGet && r.URL.Path == "/v2/sessions/sess-1": _, _ = w.Write([]byte(`{"session":{"factors":{"user":{"id":"user-42","loginName":"alice@example.com"},"password":{"verifiedAt":"2026-01-01T00:00:00Z"}}}}`)) default: http.NotFound(w, r) } })) t.Cleanup(srv.Close) c, err := zitadel.NewClient(zitadel.Conf{Issuer: srv.URL, APIBase: srv.URL, ServiceUserToken: testPAT}) require.NoError(t, err) tok, err := c.VerifyPassword(context.Background(), "alice@example.com", "ok") require.NoError(t, err) require.Equal(t, "user-42", tok.Subject) require.Equal(t, "alice@example.com", tok.Email) require.Empty(t, tok.AccessToken) } func TestNewClientDisabledWhenIssuerEmpty(t *testing.T) { t.Parallel() c, err := zitadel.NewClient(zitadel.Conf{}) require.NoError(t, err) require.Nil(t, c) } func parseForm(body string) map[string]string { vals, err := url.ParseQuery(body) if err != nil { return map[string]string{} } out := make(map[string]string, len(vals)) for k, v := range vals { if len(v) > 0 { out[k] = v[0] } } return out }