mirror of
https://github.com/hanzoai/forge.git
synced 2026-10-30 15:00:39 +00:00
Source files keep their SPDX-License-Identifier and Hanzo copyright line; the Gitea, Gogs and LFS Test Server copyrights they used to repeat now live once in NOTICE, alongside LICENSE. The header lint accepts any copyright line above the SPDX line, so it no longer demands an upstream one. Co-authored-by: Hanzo Dev <[email protected]>
310 lines
11 KiB
Go
310 lines
11 KiB
Go
// Copyright 2026 Hanzo AI, Inc. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package integration
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"net/url"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/hanzoai/git/models/organization"
|
|
"github.com/hanzoai/git/models/unittest"
|
|
user_model "github.com/hanzoai/git/models/user"
|
|
"github.com/hanzoai/git/modules/setting"
|
|
"github.com/hanzoai/git/modules/test"
|
|
"github.com/hanzoai/git/tests"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
func TestOrgTeamEmailInvite(t *testing.T) {
|
|
defer tests.PrepareTestEnv(t)()
|
|
|
|
org := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3})
|
|
team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 2})
|
|
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5})
|
|
|
|
isMember, err := organization.IsTeamMember(t.Context(), team.OrgID, team.ID, user.ID)
|
|
assert.NoError(t, err)
|
|
assert.False(t, isMember)
|
|
|
|
session := loginUser(t, "user1")
|
|
|
|
teamURL := fmt.Sprintf("/org/%s/teams/%s", org.Name, team.Name)
|
|
req := NewRequestWithValues(t, "POST", teamURL+"/action/add", map[string]string{
|
|
"uid": "1",
|
|
"uname": user.Email,
|
|
})
|
|
resp := session.MakeRequest(t, req, http.StatusSeeOther)
|
|
req = NewRequest(t, "GET", test.RedirectURL(resp))
|
|
session.MakeRequest(t, req, http.StatusOK)
|
|
|
|
// get the invite token
|
|
invites, err := organization.GetInvitesByTeamID(t.Context(), team.ID)
|
|
assert.NoError(t, err)
|
|
assert.Len(t, invites, 1)
|
|
|
|
session = loginUser(t, user.Name)
|
|
|
|
// join the team
|
|
inviteURL := "/org/invite/" + invites[0].Token
|
|
req = NewRequest(t, "POST", inviteURL)
|
|
resp = session.MakeRequest(t, req, http.StatusSeeOther)
|
|
req = NewRequest(t, "GET", test.RedirectURL(resp))
|
|
session.MakeRequest(t, req, http.StatusOK)
|
|
|
|
isMember, err = organization.IsTeamMember(t.Context(), team.OrgID, team.ID, user.ID)
|
|
assert.NoError(t, err)
|
|
assert.True(t, isMember)
|
|
}
|
|
|
|
// Check that users are redirected to accept the invitation correctly after login
|
|
func TestOrgTeamEmailInviteRedirectsExistingUser(t *testing.T) {
|
|
defer tests.PrepareTestEnv(t)()
|
|
|
|
org := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3})
|
|
team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 2})
|
|
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5})
|
|
|
|
isMember, err := organization.IsTeamMember(t.Context(), team.OrgID, team.ID, user.ID)
|
|
assert.NoError(t, err)
|
|
assert.False(t, isMember)
|
|
|
|
// create the invite
|
|
session := loginUser(t, "user1")
|
|
|
|
teamURL := fmt.Sprintf("/org/%s/teams/%s", org.Name, team.Name)
|
|
req := NewRequestWithValues(t, "POST", teamURL+"/action/add", map[string]string{
|
|
"uid": "1",
|
|
"uname": user.Email,
|
|
})
|
|
resp := session.MakeRequest(t, req, http.StatusSeeOther)
|
|
req = NewRequest(t, "GET", test.RedirectURL(resp))
|
|
session.MakeRequest(t, req, http.StatusOK)
|
|
|
|
// get the invite token
|
|
invites, err := organization.GetInvitesByTeamID(t.Context(), team.ID)
|
|
assert.NoError(t, err)
|
|
assert.Len(t, invites, 1)
|
|
|
|
// accept the invite
|
|
inviteURL := "/org/invite/" + invites[0].Token
|
|
req = NewRequest(t, "GET", "/user/login?redirect_to="+url.QueryEscape(inviteURL))
|
|
resp = MakeRequest(t, req, http.StatusOK)
|
|
|
|
req = NewRequestWithValues(t, "POST", "/user/login", map[string]string{
|
|
"user_name": "user5",
|
|
"password": "password",
|
|
})
|
|
for _, c := range resp.Result().Cookies() {
|
|
req.AddCookie(c)
|
|
}
|
|
|
|
resp = MakeRequest(t, req, http.StatusSeeOther)
|
|
assert.Equal(t, inviteURL, test.RedirectURL(resp))
|
|
|
|
// complete the login process
|
|
ch := http.Header{}
|
|
ch.Add("Cookie", strings.Join(resp.Header()["Set-Cookie"], ";"))
|
|
cr := http.Request{Header: ch}
|
|
|
|
session = emptyTestSession(t)
|
|
baseURL, err := url.Parse(setting.AppURL)
|
|
assert.NoError(t, err)
|
|
session.jar.SetCookies(baseURL, cr.Cookies())
|
|
|
|
// make the request
|
|
req = NewRequest(t, "POST", test.RedirectURL(resp))
|
|
resp = session.MakeRequest(t, req, http.StatusSeeOther)
|
|
req = NewRequest(t, "GET", test.RedirectURL(resp))
|
|
session.MakeRequest(t, req, http.StatusOK)
|
|
|
|
isMember, err = organization.IsTeamMember(t.Context(), team.OrgID, team.ID, user.ID)
|
|
assert.NoError(t, err)
|
|
assert.True(t, isMember)
|
|
}
|
|
|
|
// Check that newly signed up users are redirected to accept the invitation correctly
|
|
func TestOrgTeamEmailInviteRedirectsNewUser(t *testing.T) {
|
|
defer tests.PrepareTestEnv(t)()
|
|
|
|
org := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3})
|
|
team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 2})
|
|
|
|
// create the invite
|
|
session := loginUser(t, "user1")
|
|
|
|
teamURL := fmt.Sprintf("/org/%s/teams/%s", org.Name, team.Name)
|
|
req := NewRequestWithValues(t, "POST", teamURL+"/action/add", map[string]string{
|
|
"uid": "1",
|
|
"uname": "[email protected]",
|
|
})
|
|
resp := session.MakeRequest(t, req, http.StatusSeeOther)
|
|
req = NewRequest(t, "GET", test.RedirectURL(resp))
|
|
session.MakeRequest(t, req, http.StatusOK)
|
|
|
|
// get the invite token
|
|
invites, err := organization.GetInvitesByTeamID(t.Context(), team.ID)
|
|
assert.NoError(t, err)
|
|
assert.Len(t, invites, 1)
|
|
|
|
// accept the invite
|
|
inviteURL := "/org/invite/" + invites[0].Token
|
|
req = NewRequest(t, "GET", "/user/sign_up?redirect_to="+url.QueryEscape(inviteURL))
|
|
resp = MakeRequest(t, req, http.StatusOK)
|
|
|
|
req = NewRequestWithValues(t, "POST", "/user/sign_up", map[string]string{
|
|
"user_name": "doesnotexist",
|
|
"email": "[email protected]",
|
|
"password": "examplePassword!1",
|
|
"retype": "examplePassword!1",
|
|
})
|
|
for _, c := range resp.Result().Cookies() {
|
|
req.AddCookie(c)
|
|
}
|
|
|
|
resp = MakeRequest(t, req, http.StatusSeeOther)
|
|
assert.Equal(t, inviteURL, test.RedirectURL(resp))
|
|
|
|
// complete the signup process
|
|
ch := http.Header{}
|
|
ch.Add("Cookie", strings.Join(resp.Header()["Set-Cookie"], ";"))
|
|
cr := http.Request{Header: ch}
|
|
|
|
session = emptyTestSession(t)
|
|
baseURL, err := url.Parse(setting.AppURL)
|
|
assert.NoError(t, err)
|
|
session.jar.SetCookies(baseURL, cr.Cookies())
|
|
|
|
// make the redirected request
|
|
req = NewRequest(t, "POST", test.RedirectURL(resp))
|
|
resp = session.MakeRequest(t, req, http.StatusSeeOther)
|
|
req = NewRequest(t, "GET", test.RedirectURL(resp))
|
|
session.MakeRequest(t, req, http.StatusOK)
|
|
|
|
// get the new user
|
|
newUser, err := user_model.GetUserByName(t.Context(), "doesnotexist")
|
|
assert.NoError(t, err)
|
|
|
|
isMember, err := organization.IsTeamMember(t.Context(), team.OrgID, team.ID, newUser.ID)
|
|
assert.NoError(t, err)
|
|
assert.True(t, isMember)
|
|
}
|
|
|
|
// Check that users are redirected correctly after confirming their email
|
|
func TestOrgTeamEmailInviteRedirectsNewUserWithActivation(t *testing.T) {
|
|
// enable email confirmation temporarily
|
|
defer test.MockVariableValue(&setting.Service.RegisterEmailConfirm, true)()
|
|
defer tests.PrepareTestEnv(t)()
|
|
|
|
org := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3})
|
|
team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 2})
|
|
|
|
// user1: create the invite
|
|
session := loginUser(t, "user1")
|
|
|
|
teamURL := fmt.Sprintf("/org/%s/teams/%s", org.Name, team.Name)
|
|
req := NewRequestWithValues(t, "POST", teamURL+"/action/add", map[string]string{
|
|
"uid": "1",
|
|
"uname": "[email protected]",
|
|
})
|
|
resp := session.MakeRequest(t, req, http.StatusSeeOther)
|
|
req = NewRequest(t, "GET", test.RedirectURL(resp))
|
|
session.MakeRequest(t, req, http.StatusOK)
|
|
|
|
// get the invite token
|
|
invites, err := organization.GetInvitesByTeamID(t.Context(), team.ID)
|
|
assert.NoError(t, err)
|
|
assert.Len(t, invites, 1)
|
|
|
|
// new user: accept the invite
|
|
session = emptyTestSession(t)
|
|
|
|
inviteURL := "/org/invite/" + invites[0].Token
|
|
req = NewRequest(t, "GET", "/user/sign_up?redirect_to="+url.QueryEscape(inviteURL))
|
|
session.MakeRequest(t, req, http.StatusOK)
|
|
req = NewRequestWithValues(t, "POST", "/user/sign_up", map[string]string{
|
|
"user_name": "doesnotexist",
|
|
"email": "[email protected]",
|
|
"password": "examplePassword!1",
|
|
"retype": "examplePassword!1",
|
|
})
|
|
session.MakeRequest(t, req, http.StatusOK)
|
|
|
|
user, err := user_model.GetUserByName(t.Context(), "doesnotexist")
|
|
assert.NoError(t, err)
|
|
|
|
activationCode := user_model.GenerateUserTimeLimitCode(&user_model.TimeLimitCodeOptions{Purpose: user_model.TimeLimitCodeActivateAccount}, user)
|
|
activateURL := "/user/activate?code=" + activationCode
|
|
req = NewRequestWithValues(t, "POST", activateURL, map[string]string{
|
|
"password": "examplePassword!1",
|
|
})
|
|
|
|
resp = session.MakeRequest(t, req, http.StatusSeeOther)
|
|
// should be redirected to accept the invite
|
|
assert.Equal(t, inviteURL, test.RedirectURL(resp))
|
|
|
|
req = NewRequest(t, "POST", test.RedirectURL(resp))
|
|
resp = session.MakeRequest(t, req, http.StatusSeeOther)
|
|
req = NewRequest(t, "GET", test.RedirectURL(resp))
|
|
session.MakeRequest(t, req, http.StatusOK)
|
|
|
|
isMember, err := organization.IsTeamMember(t.Context(), team.OrgID, team.ID, user.ID)
|
|
assert.NoError(t, err)
|
|
assert.True(t, isMember)
|
|
}
|
|
|
|
// Test that a logged-in user who navigates to the sign-up link is then redirected using redirect_to
|
|
// For example: an invite may have been created before the user account was created, but they may be
|
|
// accepting the invite after having created an account separately
|
|
func TestOrgTeamEmailInviteRedirectsExistingUserWithLogin(t *testing.T) {
|
|
defer tests.PrepareTestEnv(t)()
|
|
|
|
org := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3})
|
|
team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 2})
|
|
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5})
|
|
|
|
isMember, err := organization.IsTeamMember(t.Context(), team.OrgID, team.ID, user.ID)
|
|
assert.NoError(t, err)
|
|
assert.False(t, isMember)
|
|
|
|
// create the invite
|
|
session := loginUser(t, "user1")
|
|
|
|
teamURL := fmt.Sprintf("/org/%s/teams/%s", org.Name, team.Name)
|
|
req := NewRequestWithValues(t, "POST", teamURL+"/action/add", map[string]string{
|
|
"uid": "1",
|
|
"uname": user.Email,
|
|
})
|
|
resp := session.MakeRequest(t, req, http.StatusSeeOther)
|
|
req = NewRequest(t, "GET", test.RedirectURL(resp))
|
|
session.MakeRequest(t, req, http.StatusOK)
|
|
|
|
// get the invite token
|
|
invites, err := organization.GetInvitesByTeamID(t.Context(), team.ID)
|
|
assert.NoError(t, err)
|
|
assert.Len(t, invites, 1)
|
|
|
|
// note: the invited user has logged in
|
|
session = loginUser(t, "user5")
|
|
|
|
// accept the invite (note: this uses the sign_up url)
|
|
inviteURL := "/org/invite/" + invites[0].Token
|
|
req = NewRequest(t, "GET", "/user/sign_up?redirect_to="+url.QueryEscape(inviteURL))
|
|
resp = session.MakeRequest(t, req, http.StatusSeeOther)
|
|
assert.Equal(t, inviteURL, test.RedirectURL(resp))
|
|
|
|
// make the request
|
|
req = NewRequest(t, "POST", test.RedirectURL(resp))
|
|
resp = session.MakeRequest(t, req, http.StatusSeeOther)
|
|
req = NewRequest(t, "GET", test.RedirectURL(resp))
|
|
session.MakeRequest(t, req, http.StatusOK)
|
|
|
|
isMember, err = organization.IsTeamMember(t.Context(), team.OrgID, team.ID, user.ID)
|
|
assert.NoError(t, err)
|
|
assert.True(t, isMember)
|
|
}
|