package dex import ( "context" "encoding/json" "io" "net/http" "net/http/httptest" "testing" "github.com/stretchr/testify/require" ) // newTestClient points a PasswordClient at an httptest server, using that // server's TLS client so the in-cluster TLS path is exercised without a real CA. func newTestClient(srv *httptest.Server) *PasswordClient { return newClient(srv.URL, "test-token", srv.Client()) } func TestCreatePasswordSuccess(t *testing.T) { var gotAuth, gotPath, gotMethod string var gotBody password srv := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { gotAuth, gotPath, gotMethod = r.Header.Get("Authorization"), r.URL.Path, r.Method b, _ := io.ReadAll(r.Body) _ = json.Unmarshal(b, &gotBody) w.WriteHeader(http.StatusCreated) _, _ = w.Write([]byte(`{"kind":"Password"}`)) })) defer srv.Close() err := newTestClient(srv).CreatePassword(context.Background(), "New.User@Example.com", "$2a$12$abcdefghijklmnopqrstuv", "user-uuid-1") require.NoError(t, err) require.Equal(t, http.MethodPost, gotMethod) require.Equal(t, passwordsPath, gotPath) require.Equal(t, "Bearer test-token", gotAuth) // Email/username carry the raw address; the CR name is sanitised + lowercased. require.Equal(t, "New.User@Example.com", gotBody.Email) require.Equal(t, "New.User@Example.com", gotBody.Username) require.Equal(t, "user-uuid-1", gotBody.UserID) require.Equal(t, "new-dot-user-at-example-dot-com", gotBody.Metadata["name"]) require.Equal(t, "auth", gotBody.Metadata["namespace"]) // Hash is stored as the raw bcrypt string — Dex compares it directly. require.Equal(t, "$2a$12$abcdefghijklmnopqrstuv", gotBody.Hash) } func TestCreatePasswordConflict(t *testing.T) { srv := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusConflict) })) defer srv.Close() err := newTestClient(srv).CreatePassword(context.Background(), "dup@example.com", "$2a$12$x", "u") require.ErrorIs(t, err, ErrPasswordExists) } func TestCreatePasswordForbidden(t *testing.T) { srv := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusForbidden) })) defer srv.Close() err := newTestClient(srv).CreatePassword(context.Background(), "x@example.com", "$2a$12$x", "u") require.ErrorIs(t, err, ErrForbidden) } func TestCreatePasswordUnexpectedStatus(t *testing.T) { srv := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusInternalServerError) _, _ = w.Write([]byte("boom")) })) defer srv.Close() err := newTestClient(srv).CreatePassword(context.Background(), "x@example.com", "$2a$12$x", "u") require.Error(t, err) require.NotErrorIs(t, err, ErrPasswordExists) require.NotErrorIs(t, err, ErrForbidden) require.Contains(t, err.Error(), "500") } func TestNewPasswordClientNotInCluster(t *testing.T) { // In the test environment the SA token mount does not exist. _, err := NewPasswordClient() require.ErrorIs(t, err, ErrNotInCluster) } func TestPasswordName(t *testing.T) { cases := map[string]string{ "Alice@Example.com": "alice-at-example-dot-com", "a.b+c@gmail.com": "a-dot-b-c-at-gmail-dot-com", "UPPER@DOMAIN.IO": "upper-at-domain-dot-io", } for in, want := range cases { require.Equal(t, want, passwordName(in), in) } }