chore: Add distant federation server mock (#7115)

Encapsulates the federation server counterpart & makes the test more configurable.

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/7115
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
Co-authored-by: Michael Jerger <michael.jerger@meissa-gmbh.de>
Co-committed-by: Michael Jerger <michael.jerger@meissa-gmbh.de>
This commit is contained in:
Michael Jerger 2025-03-05 14:57:07 +00:00 committed by Gusted
parent c1399947f4
commit e39b98ea51
3 changed files with 141 additions and 109 deletions

View file

@ -5,9 +5,7 @@ package integration
import (
"fmt"
"io"
"net/http"
"net/http/httptest"
"strings"
"testing"
@ -21,6 +19,7 @@ import (
fm "code.gitea.io/gitea/modules/forgefed"
"code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/test"
"code.gitea.io/gitea/modules/validation"
gitea_context "code.gitea.io/gitea/services/context"
repo_service "code.gitea.io/gitea/services/repository"
@ -278,59 +277,8 @@ func TestRepoFollowing(t *testing.T) {
setting.Federation.Enabled = false
}()
federatedRoutes := http.NewServeMux()
federatedRoutes.HandleFunc("/.well-known/nodeinfo",
func(res http.ResponseWriter, req *http.Request) {
// curl -H "Accept: application/json" https://federated-repo.prod.meissa.de/.well-known/nodeinfo
responseBody := fmt.Sprintf(`{"links":[{"href":"http://%s/api/v1/nodeinfo","rel":"http://nodeinfo.diaspora.software/ns/schema/2.1"}]}`, req.Host)
t.Logf("response: %s", responseBody)
// TODO: as soon as content-type will become important: content-type: application/json;charset=utf-8
fmt.Fprint(res, responseBody)
})
federatedRoutes.HandleFunc("/api/v1/nodeinfo",
func(res http.ResponseWriter, req *http.Request) {
// curl -H "Accept: application/json" https://federated-repo.prod.meissa.de/api/v1/nodeinfo
responseBody := fmt.Sprintf(`{"version":"2.1","software":{"name":"forgejo","version":"1.20.0+dev-3183-g976d79044",` +
`"repository":"https://codeberg.org/forgejo/forgejo.git","homepage":"https://forgejo.org/"},` +
`"protocols":["activitypub"],"services":{"inbound":[],"outbound":["rss2.0"]},` +
`"openRegistrations":true,"usage":{"users":{"total":14,"activeHalfyear":2}},"metadata":{}}`)
fmt.Fprint(res, responseBody)
})
repo1InboxReceivedLike := false
federatedRoutes.HandleFunc("/api/v1/activitypub/repository-id/1/inbox/",
func(res http.ResponseWriter, req *http.Request) {
if req.Method != "POST" {
t.Errorf("Unhandled request: %q", req.URL.EscapedPath())
}
buf := new(strings.Builder)
_, err := io.Copy(buf, req.Body)
if err != nil {
t.Errorf("Error reading body: %q", err)
}
like := fm.ForgeLike{}
err = like.UnmarshalJSON([]byte(buf.String()))
if err != nil {
t.Errorf("Error unmarshalling ForgeLike: %q", err)
}
if isValid, err := validation.IsValid(like); !isValid {
t.Errorf("ForgeLike is not valid: %q", err)
}
activityType := like.Type
object := like.Object.GetLink().String()
isLikeType := activityType == "Like"
isCorrectObject := strings.HasSuffix(object, "/api/v1/activitypub/repository-id/1")
if !isLikeType || !isCorrectObject {
t.Errorf("Activity is not a like for this repo")
}
repo1InboxReceivedLike = true
})
federatedRoutes.HandleFunc("/",
func(res http.ResponseWriter, req *http.Request) {
t.Errorf("Unhandled request: %q", req.URL.EscapedPath())
})
federatedSrv := httptest.NewServer(federatedRoutes)
mock := test.NewFederationServerMock()
federatedSrv := mock.DistantServer(t)
defer federatedSrv.Close()
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
@ -363,8 +311,24 @@ func TestRepoFollowing(t *testing.T) {
req := NewRequestWithValues(t, "POST", link, map[string]string{
"_csrf": GetCSRF(t, session, repoLink),
})
assert.False(t, repo1InboxReceivedLike)
session.MakeRequest(t, req, http.StatusOK)
assert.True(t, repo1InboxReceivedLike)
// Verify distant server received a like activity
like := fm.ForgeLike{}
err := like.UnmarshalJSON([]byte(mock.LastPost))
if err != nil {
t.Errorf("Error unmarshalling ForgeLike: %q", err)
}
if isValid, err := validation.IsValid(like); !isValid {
t.Errorf("ForgeLike is not valid: %q", err)
}
activityType := like.Type
object := like.Object.GetLink().String()
isLikeType := activityType == "Like"
isCorrectObject := strings.HasSuffix(object, "/api/v1/activitypub/repository-id/1")
if !isLikeType || !isCorrectObject {
t.Errorf("Activity is not a like for this repo")
}
})
}