blob: a21e0ff3e2fd84d15db75202a53b0a51c6a5efb1 [file] [log] [blame]
package testutils
/*
Utilities for mocking requests to Gitiles.
*/
import (
"context"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"io/fs"
"os"
"path"
"strconv"
"github.com/stretchr/testify/require"
"go.skia.org/infra/go/git"
"go.skia.org/infra/go/gitiles"
"go.skia.org/infra/go/gitiles/mocks"
"go.skia.org/infra/go/mockhttpclient"
"go.skia.org/infra/go/sktest"
"go.skia.org/infra/go/testutils"
"go.skia.org/infra/go/vfs"
)
// XSS protection line which is added by the Gitiles server and removed by our
// client code.
const XSSLine = ")]}'\n"
type MockRepo struct {
URLMock *mockhttpclient.URLMock
repo git.GitDir
t sktest.TestingT
url string
}
func NewMockRepo(t sktest.TestingT, url string, repo git.GitDir, c *mockhttpclient.URLMock) *MockRepo {
return &MockRepo{
URLMock: c,
repo: repo,
t: t,
url: url,
}
}
func (mr *MockRepo) Empty() bool {
return mr.URLMock.Empty()
}
func MockReadFile(t sktest.TestingT, c *mockhttpclient.URLMock, repoUrl, srcPath, ref string, contents []byte, stat fs.FileInfo) {
body := make([]byte, base64.StdEncoding.EncodedLen(len(contents)))
base64.StdEncoding.Encode(body, contents)
url := fmt.Sprintf(gitiles.DownloadURL, repoUrl, ref, srcPath)
md := mockhttpclient.MockGetDialogue(body)
typ := git.ObjectTypeBlob
if stat.IsDir() {
typ = git.ObjectTypeTree
}
md.ResponseHeader(gitiles.ModeHeader, strconv.FormatInt(int64(stat.Mode()), 8))
md.ResponseHeader(gitiles.TypeHeader, string(typ))
c.MockOnce(url, md)
}
func (mr *MockRepo) MockReadFile(ctx context.Context, srcPath, ref string) {
fs, err := mr.repo.VFS(ctx, ref)
require.NoError(mr.t, err)
defer func() {
require.NoError(mr.t, fs.Close(ctx))
}()
f, err := fs.Open(ctx, srcPath)
require.NoError(mr.t, err)
contents, err := io.ReadAll(vfs.WithContext(ctx, f))
require.NoError(mr.t, err)
st, err := f.Stat(ctx)
require.NoError(mr.t, err)
MockReadFile(mr.t, mr.URLMock, mr.url, srcPath, ref, contents, st)
}
func getCommit(t sktest.TestingT, ctx context.Context, repo git.GitDir, ref string) *gitiles.Commit {
details, err := repo.Details(ctx, ref)
require.NoError(t, err)
rv, err := gitiles.LongCommitToCommit(details)
require.NoError(t, err)
return rv
}
func MockGetCommit(t sktest.TestingT, c *mockhttpclient.URLMock, repoUrl, ref string, commit *gitiles.Commit) {
b, err := json.Marshal(commit)
require.NoError(t, err)
b = append([]byte(XSSLine), b...)
url := fmt.Sprintf(gitiles.CommitURLJSON, repoUrl, ref)
c.MockOnce(url, mockhttpclient.MockGetDialogue(b))
}
func (mr *MockRepo) MockGetCommit(ctx context.Context, ref string) {
c := getCommit(mr.t, ctx, mr.repo, ref)
MockGetCommit(mr.t, mr.URLMock, mr.url, ref, c)
}
func MockBranches(t sktest.TestingT, c *mockhttpclient.URLMock, repoUrl string, branches []*git.Branch) {
res := make(gitiles.RefsMap, len(branches))
for _, branch := range branches {
res[branch.Name] = gitiles.Ref{
Value: branch.Head,
}
}
b, err := json.Marshal(res)
require.NoError(t, err)
b = append([]byte(XSSLine), b...)
url := fmt.Sprintf(gitiles.RefsURL, repoUrl)
c.MockOnce(url, mockhttpclient.MockGetDialogue(b))
}
func (mr *MockRepo) MockBranches(ctx context.Context) {
branches, err := mr.repo.Branches(ctx)
require.NoError(mr.t, err)
MockBranches(mr.t, mr.URLMock, mr.url, branches)
}
func MockLog(t sktest.TestingT, c *mockhttpclient.URLMock, repoUrl, logExpr string, log *gitiles.Log, opts ...gitiles.LogOption) {
path, query, _, err := gitiles.LogOptionsToQuery(opts)
require.NoError(t, err)
b, err := json.Marshal(log)
require.NoError(t, err)
b = append([]byte(XSSLine), b...)
if path != "" {
logExpr += "/" + path
}
url := fmt.Sprintf(gitiles.LogURL, repoUrl, logExpr)
if query != "" {
url += "&" + query
}
c.MockOnce(url, mockhttpclient.MockGetDialogue(b))
}
func (mr *MockRepo) MockLog(ctx context.Context, logExpr string, opts ...gitiles.LogOption) {
path, _, _, err := gitiles.LogOptionsToQuery(opts)
require.NoError(mr.t, err)
args := []string{logExpr}
if path != "" {
args = append(args, "--", path)
}
revlist, err := mr.repo.RevList(ctx, args...)
require.NoError(mr.t, err)
log := &gitiles.Log{
Log: make([]*gitiles.Commit, len(revlist)),
}
for idx, hash := range revlist {
log.Log[idx] = getCommit(mr.t, ctx, mr.repo, hash)
}
MockLog(mr.t, mr.URLMock, mr.url, logExpr, log, opts...)
}
type MockReadObjectInfo struct {
path string
fi fs.FileInfo
contents []byte
}
func MockReadObject(g *mocks.GitilesRepo, revision string, obj *MockReadObjectInfo) {
g.On("ReadObject", testutils.AnyContext, obj.path, revision).Return(obj.fi, obj.contents, nil).Once()
}
func MockReadObject_File(g *mocks.GitilesRepo, revision, name, contents string) {
contentsBytes := []byte(contents)
MockReadObject(g, revision, &MockReadObjectInfo{
path: name,
fi: vfs.FileInfo{
Name: path.Base(name),
Size: int64(len(contentsBytes)),
Mode: 0644,
IsDir: false,
}.Get(),
contents: contentsBytes,
})
}
func MockReadObject_Dir(g *mocks.GitilesRepo, revision, name string, contents []string) {
contentsStr := ""
for _, name := range contents {
contentsStr += fmt.Sprintf("0644 blob somehash %s\n", name)
}
contentsBytes := []byte(contentsStr)
MockReadObject(g, revision, &MockReadObjectInfo{
path: name,
fi: vfs.FileInfo{
Name: path.Base(name),
Size: int64(len(contentsBytes)),
Mode: 0644 | os.ModeDir,
IsDir: true,
}.Get(),
contents: contentsBytes,
})
}