Skip to content
quarry Artifact [2aed83d89b]
Login

Artifact [2aed83d89b]

Artifact 2aed83d89b22062c9478b59ae751a799602d6e335b87bbb1bc4a6e0967965891:


package api

import (
	"context"
	"errors"
	"testing"

	"craftdesign.group/quarry/internal/oltp"
	"craftdesign.group/quarry/internal/ops"
	"craftdesign.group/quarry/internal/xfer"
)

type memFossilUsers struct {
	got   []oltp.FossilUser
	links map[string]string
}

func (m *memFossilUsers) ReplaceFossilUsers(_ string, u []oltp.FossilUser) (int, int, error) {
	m.got = u
	return len(u), 0, nil
}
func (m *memFossilUsers) FossilUsers(string) ([]oltp.FossilUser, error) { return m.got, nil }
func (m *memFossilUsers) LinkFossilUser(_, _, login, account string) (string, error) {
	for _, u := range m.got {
		if u.Login == login {
			m.links[login] = account
			return "", nil
		}
	}
	return "", oltp.ErrNoFossilUser
}

type fetchCall struct{ peer, code, login string }

func fossilUserDeps(t *testing.T, calls *[]fetchCall) (*ops.Registry, *memFossilUsers) {
	t.Helper()
	store := &memFossilUsers{links: map[string]string{}}
	r := ops.NewRegistry()
	if err := RegisterAll(r, Deps{Version: "v", Repos: testRepos(wikiIndex(t)),
		FossilUsers: store,
		ProjectCode: func(string) (string, error) { return "code-test", nil },
		FetchUsers: func(peer, code, login, _ string) ([]xfer.RemoteUser, error) {
			*calls = append(*calls, fetchCall{peer, code, login})
			return []xfer.RemoteUser{{Login: "alice", Caps: "ei", PWHash: "secret-hash"}}, nil
		}}); err != nil {
		t.Fatal(err)
	}
	return r, store
}

func asCaps(caps string) context.Context {
	return ops.WithCaps(ops.WithUser(context.Background(), "dan"), oltp.CapSet(caps))
}

func TestFossilUserSyncFetchesWithTheGivenLoginAndKeepsNoHash(t *testing.T) {
	var calls []fetchCall
	r, store := fossilUserDeps(t, &calls)
	out, err := r.Invoke(asCaps("s"), "repo.fossil.users.sync", fossilUsersSyncIn{
		Repo: "test", URL: "https://fossil.example.org/r", Login: "root", Password: "pw"})
	if err != nil {
		t.Fatal(err)
	}
	if len(calls) != 1 || calls[0] != (fetchCall{"https://fossil.example.org/r", "code-test", "root"}) {
		t.Fatalf("fetch calls = %+v", calls)
	}
	if got := out.(fossilUsersSyncOut); got.Users != 1 || len(store.got) != 1 || store.got[0].Login != "alice" {
		t.Fatalf("sync out %+v, stored %+v", got, store.got)
	}
}

// The server makes the request, so only a site administrator may aim
// it somewhere other than the repository's own fossil server.
func TestFossilUserSyncRefusesAnotherServerToARepositoryAdmin(t *testing.T) {
	var calls []fetchCall
	r, _ := fossilUserDeps(t, &calls)
	_, err := r.Invoke(asCaps("a"), "repo.fossil.users.sync", fossilUsersSyncIn{
		Repo: "test", URL: "http://127.0.0.1:4222/", Login: "root", Password: "pw"})
	if !errors.Is(err, ops.ErrForbidden) {
		t.Fatalf("repository admin with a foreign URL: %v", err)
	}
	_, err = r.Invoke(asCaps("a"), "repo.fossil.users.sync", fossilUsersSyncIn{
		Repo: "test", Login: "root", Password: "pw"})
	if !errors.Is(err, ops.ErrInvalid) {
		t.Fatalf("no URL and no upstream: %v", err)
	}
	_, err = r.Invoke(asCaps("s"), "repo.fossil.users.sync", fossilUsersSyncIn{
		Repo: "test", URL: "file:///etc/passwd", Login: "root", Password: "pw"})
	if !errors.Is(err, ops.ErrInvalid) {
		t.Fatalf("a non-http URL: %v", err)
	}
	if len(calls) != 0 {
		t.Fatalf("a refused sync still fetched: %+v", calls)
	}
}

func TestFossilUserLinkNamesAMissingLogin(t *testing.T) {
	var calls []fetchCall
	r, store := fossilUserDeps(t, &calls)
	store.got = []oltp.FossilUser{{Login: "alice", Caps: "i"}}
	if _, err := r.Invoke(asCaps("a"), "repo.fossil.users.link", fossilUserLinkIn{Repo: "test", Login: "alice", Account: "al"}); err != nil {
		t.Fatal(err)
	}
	if store.links["alice"] != "al" {
		t.Fatalf("links = %v", store.links)
	}
	_, err := r.Invoke(asCaps("a"), "repo.fossil.users.link", fossilUserLinkIn{Repo: "test", Login: "zed", Account: "al"})
	if !errors.Is(err, ops.ErrNotFound) {
		t.Fatalf("link of a login that was never synced: %v", err)
	}
}