Skip to content
quarry Artifact [de8a1a7653]
Login

Artifact [de8a1a7653]

Artifact de8a1a76539951ea1982573aadc56ff63f09f6c9c6d90e742c1665d919734385:


package api

import (
	"context"
	"testing"

	"craftdesign.group/quarry/internal/markup"
)

// fakeCaps answers a fixed letter set.
type fakeCaps string

func (c fakeCaps) HasAny(letters string) bool {
	for _, l := range letters {
		for _, have := range string(c) {
			if l == have {
				return true
			}
		}
	}
	return false
}

// The resolver table of RFC 0012 Phase 1: each token shape reaches the
// right page, and a token naming a repository the reader cannot see
// reaches nothing at all.
func TestResolveTokenTable(t *testing.T) {
	// A scope whose CanRead admits "open" and refuses "secret", and
	// whose caps grant everything on what it admits.
	d := Deps{
		Repos:   &Repos{},
		CanRead: func(_ context.Context, repo string) bool { return repo == "open" },
		CapsFor: func(_, _ string) CapSet { return fakeCaps(anyCapLetters) },
	}
	r := resolver{repo: "demo", scope: &refScope{ctx: context.Background(), d: d}}

	for _, tt := range []struct {
		token string
		href  string // "" means: must not resolve
	}{
		{token: "#412", href: "/r/demo/chat/412"},
		{token: "@alice", href: "/people/alice"},
		{token: "/r/demo/ticket/xy9", href: "/r/demo/ticket/xy9"},
		{token: "https://q.example/r/demo/landing/ab12", href: "/r/demo/landing/ab12"},
		// Cross-repo: "open" is readable, "secret" is not. Neither is
		// registered as a repository here, so only the refusal is
		// asserted — the admitted one is exercised in the harness.
		{token: "secret:#9"},
		{token: "secret:[a91f3c2e91]"},
		{token: "notatoken"},
	} {
		tok, ok := markup.ParseRef(tt.token)
		if !ok {
			if tt.href != "" {
				t.Fatalf("%q did not parse", tt.token)
			}
			continue
		}
		href, ok := r.ResolveToken(tok)
		if tt.href == "" {
			if ok {
				t.Fatalf("%q resolved to %q; it must stay plain text", tt.token, href)
			}
			continue
		}
		if !ok || href != tt.href {
			t.Fatalf("%q resolved to %q (ok=%v), want %q", tt.token, href, ok, tt.href)
		}
	}
}

// A resolver with no scope is the render path that has no request in
// hand: it must not reach across repositories at all.
func TestResolveTokenWithoutScopeStaysHome(t *testing.T) {
	r := resolver{repo: "demo"}
	tok, _ := markup.ParseRef("other:#7")
	if href, ok := r.ResolveToken(tok); ok {
		t.Fatalf("scopeless resolver crossed repositories: %q", href)
	}
	tok, _ = markup.ParseRef("#7")
	if href, ok := r.ResolveToken(tok); !ok || href != "/r/demo/chat/7" {
		t.Fatalf("same-repo token = %q (ok=%v)", href, ok)
	}
}

// refCap reaches for the letter the TARGET's own op is registered with,
// so a preview can never say more than the page it links to would.
func TestRefCapMatchesTheTargetOp(t *testing.T) {
	for kind, want := range map[string]string{
		markup.RefChat: "C", markup.RefCheckin: "o", markup.RefHash: "o",
		markup.RefTicket: "r", markup.RefLanding: "2", markup.RefForum: "2",
		markup.RefWiki: "j", markup.RefPerson: "",
	} {
		if got := refCap(kind); got != want {
			t.Fatalf("refCap(%q) = %q, want %q", kind, got, want)
		}
	}
}

// The quote and the reply count come from one pass over one slice.
func TestChatQuoteFillsBothSides(t *testing.T) {
	msgs := []chatMsgOut{
		{Seq: 4, Kind: "msg", User: "alice", Text: "first line\nsecond"},
		{Seq: 5, Kind: "msg", User: "bob", Ref: 4, Text: "agreed"},
		{Seq: 6, Kind: "msg", User: "carol", Ref: 4, Text: "same"},
		{Seq: 7, Kind: "msg", User: "dan", Ref: 99, Text: "orphan"},
	}
	chatQuote(msgs)
	if msgs[0].Replies != 2 {
		t.Fatalf("replies = %d, want 2", msgs[0].Replies)
	}
	if msgs[1].RefUser != "alice" || msgs[1].RefLine != "first line" {
		t.Fatalf("quote = %q/%q", msgs[1].RefUser, msgs[1].RefLine)
	}
	if msgs[3].RefUser != "" {
		t.Fatalf("a quote was invented for an unsourceable reply")
	}
}