Skip to content
quarry Artifact [ee53366ee6]
Login

Artifact [ee53366ee6]

Artifact ee53366ee65eaf1bf9aec091c7c95dc4b99a5fad84ba97a21c26a2c51770d28c:


package api

import (
	"os"
	"path/filepath"
	"strings"
	"testing"
	"time"

	"craftdesign.group/quarry/internal/oltp"
	"craftdesign.group/quarry/internal/uv"
)

// A sync token ceilinged to y (RFC 0021) signs a stock fossil client's
// unversioned push, and the same token signing a check-in is refused
// whole: the login card resolves to y without i, and the gate moved to
// the cards.
func TestAYOnlyTokenPushesUnversionedButNotCheckIns(t *testing.T) {
	g := newUVFossilRig(t, 1<<20)
	// The forge's own credential store behind the front, in place of the
	// rig's password fake.
	g.db.SyncProjectCode = func(string) (string, bool) { return g.code, true }
	if err := g.db.AddAccount("pub", "pw-pub-unused", false); err != nil {
		t.Fatal(err)
	}
	if err := g.db.SetCaps("pub", "test", "oiy"); err != nil {
		t.Fatal(err)
	}
	token, _, err := g.db.MintToken("pub", oltp.TokenSpec{Name: "release", Scopes: []string{oltp.ScopeSync}, Ceiling: "y"})
	if err != nil {
		t.Fatal(err)
	}
	g.front.SetAuth(g.db)
	withToken := strings.Replace(g.base, "http://", "http://pub:"+token+"@", 1)

	a := g.clone("a")
	g.uvAdd(a, "release.tar", "bytes\n", "2026-01-01 00:00:00")
	out, err := g.fossil(filepath.Dir(a), "sync", "-u", withToken, "-R", a)
	o, inBucket := g.objects.has(uv.Key("test", "release.tar"))
	check(t, err == nil && inBucket && string(o.body) == "bytes\n",
		"a y-only token publishes an unversioned file",
		"uv push with a y-only token: err %v, in bucket %v\n%s", err, inBucket, out)

	co := t.TempDir()
	g.must(co, "settings", "autosync", "off", "-R", a)
	g.must(co, "open", a, "--nosync")
	if err := os.WriteFile(filepath.Join(co, "NEW.md"), []byte("by a uv token\n"), 0o644); err != nil {
		t.Fatal(err)
	}
	g.must(co, "add", "NEW.md")
	g.fossil(co, "commit", "-m", "by a uv token", "--no-warnings")
	out, _ = g.fossil(co, "push", withToken, "--once")
	check(t, !strings.Contains(g.timeline(), "by a uv token") && strings.Contains(out, "unversioned files only"),
		"the same token pushing a check-in is refused whole",
		"check-in push with a y-only token:\n%s", out)
}

// Two sync tokens on one account are two sessions: a stock fossil push
// signed with either lands, and revoking one leaves the other pushing.
// This is the concurrent-sessions case that one-token-per-account broke.
func TestTwoSyncTokensPushAndRevokingOneLeavesTheOther(t *testing.T) {
	g := newUVFossilRig(t, 1<<20)
	g.db.SyncProjectCode = func(string) (string, bool) { return g.code, true }
	if err := g.db.AddAccount("dev", "pw-dev-unused", false); err != nil {
		t.Fatal(err)
	}
	if err := g.db.SetCaps("dev", "test", "oi"); err != nil {
		t.Fatal(err)
	}
	one, tokOne, err := g.db.MintToken("dev", oltp.TokenSpec{Name: "session 1", Scopes: []string{oltp.ScopeSync}})
	if err != nil {
		t.Fatal(err)
	}
	two, _, err := g.db.MintToken("dev", oltp.TokenSpec{Name: "session 2", Scopes: []string{oltp.ScopeSync}})
	if err != nil {
		t.Fatal(err)
	}
	g.front.SetAuth(g.db)
	as := func(token string) string { return strings.Replace(g.base, "http://", "http://dev:"+token+"@", 1) }

	co := t.TempDir()
	a := filepath.Join(t.TempDir(), "a.fossil")
	g.must(filepath.Dir(a), "clone", as(one), a)
	g.must(co, "settings", "autosync", "off", "-R", a)
	g.must(co, "open", a, "--nosync")
	commit := func(msg string) {
		t.Helper()
		// A file per commit: same size and second would read as unchanged.
		name := strings.ReplaceAll(msg, " ", "-") + ".md"
		if err := os.WriteFile(filepath.Join(co, name), []byte(msg+"\n"), 0o644); err != nil {
			t.Fatal(err)
		}
		g.must(co, "add", name)
		g.must(co, "commit", "-m", msg, "--no-warnings")
	}
	commit("pushed with one")
	out, err := g.fossil(co, "push", as(one), "--once")
	check(t, err == nil && strings.Contains(g.timeline(), "pushed with one"),
		"a push signed with the first token lands", "push with token one: %v\n%s", err, out)
	commit("pushed with two")
	out, err = g.fossil(co, "push", as(two), "--once")
	check(t, err == nil && strings.Contains(g.timeline(), "pushed with two"),
		"a push signed with the second token lands", "push with token two: %v\n%s", err, out)

	if err := g.db.RevokeToken("dev", tokOne.ID); err != nil {
		t.Fatal(err)
	}
	commit("after the revoke")
	out, _ = g.fossil(co, "push", as(one), "--once")
	check(t, !strings.Contains(g.timeline(), "after the revoke"),
		"the revoked token's push is refused", "push with a revoked token landed:\n%s", out)
	out, err = g.fossil(co, "push", as(two), "--once")
	check(t, err == nil && strings.Contains(g.timeline(), "after the revoke"),
		"the other token still pushes", "push with the surviving token: %v\n%s", err, out)
}

// A RUN TOKEN PUBLISHES WHILE ITS RUN LIVES AND NOT AFTER (RFC 0021
// §runs). A stock fossil client is told the login run-<id> and the
// token as its password and needs nothing else: the front reads the
// secret from the run's row. y alone pushes a uv card, never a
// check-in; the run's close, or a cancel, ends it at once.
func TestARunTokenPublishesWhileItsRunLives(t *testing.T) {
	g := newUVFossilRig(t, 1<<20)
	g.db.SyncProjectCode = func(string) (string, bool) { return g.code, true }
	for _, id := range []string{"r1", "r2"} {
		if err := g.db.AddCIRun(oltp.CIRun{RunID: id, Repo: "test", Checkin: "c", Workflow: "w", Pipeline: "ci/release.yml", Status: "running"}); err != nil {
			t.Fatal(err)
		}
	}
	mint := func(run string) string {
		login, value, _, err := g.db.MintRunToken(oltp.RunTokenSpec{Run: run, Repo: "test", Job: "publish", Letters: "y", Expires: time.Now().Add(time.Hour)})
		if err != nil {
			t.Fatal(err)
		}
		return strings.Replace(g.base, "http://", "http://"+login+":"+value+"@", 1)
	}
	g.front.SetAuth(g.db)
	as := mint("r1")

	a := g.clone("a")
	g.uvAdd(a, "release.tar", "bytes\n", "2026-01-01 00:00:00")
	out, err := g.fossil(filepath.Dir(a), "sync", "-u", as, "-R", a)
	o, inBucket := g.objects.has(uv.Key("test", "release.tar"))
	check(t, err == nil && inBucket && string(o.body) == "bytes\n",
		"a y run token publishes an unversioned file",
		"uv push with a run token: err %v, in bucket %v\n%s", err, inBucket, out)

	co := t.TempDir()
	g.must(co, "settings", "autosync", "off", "-R", a)
	g.must(co, "open", a, "--nosync")
	if err := os.WriteFile(filepath.Join(co, "NEW.md"), []byte("by a run token\n"), 0o644); err != nil {
		t.Fatal(err)
	}
	g.must(co, "add", "NEW.md")
	g.fossil(co, "commit", "-m", "by a run token", "--no-warnings")
	out, _ = g.fossil(co, "push", as, "--once")
	check(t, !strings.Contains(g.timeline(), "by a run token") && strings.Contains(out, "unversioned files only"),
		"the run token pushing a check-in is refused whole",
		"check-in push with a uv: write run token:\n%s", out)

	if err := g.db.CompleteCIRun("r1", "succeeded", ""); err != nil {
		t.Fatal(err)
	}
	g.uvAdd(a, "late.tar", "late\n", "2026-01-02 00:00:00")
	out, _ = g.fossil(filepath.Dir(a), "sync", "-u", as, "-R", a)
	_, late := g.objects.has(uv.Key("test", "late.tar"))
	check(t, !late && strings.Contains(out, "not authorized"), "the run token is refused once its run closed",
		"uv push after the run closed landed:\n%s", out)

	as = mint("r2")
	if err := g.db.RevokeRunTokens("r2"); err != nil { // the cancel's revoke
		t.Fatal(err)
	}
	out, _ = g.fossil(filepath.Dir(a), "sync", "-u", as, "-R", a)
	_, late = g.objects.has(uv.Key("test", "late.tar"))
	check(t, !late && strings.Contains(out, "not authorized"), "the run token is refused once its run is cancelled",
		"uv push after the cancel landed:\n%s", out)
}