~bigbes/sr-ht-dolt

3843eb55bacf238a627efe60a88763af293d2d7d — Eugene Blikh 3 days ago a674ddb
remoteapi: prove an empty store takes an unrelated first push

This file had not compiled since the logrus->slog migration removed the
package-level `logger` its serve goroutines still called. The
`integration` build tag keeps it out of every default build, test and vet
run, so nothing ever said so — the suite simply never ran again.

With it compiling, add the scenario the storage layer's contract is
actually about: a database provisioned empty ahead of its first push (the
web create form, or git.sr.ht's hook through /internal/repos) accepts a
push from a local database with a root commit of its own, with no
--force. Mutating the provisioning to InitStore fails it exactly as a
user hits it today: "unknown push error; no common ancestor".
1 files changed, 61 insertions(+), 3 deletions(-)

M remoteapi/integration_test.go
M remoteapi/integration_test.go => remoteapi/integration_test.go +61 -3
@@ 11,7 11,12 @@
//	(5) keypair (Bearer EdDSA JWT) clone of a PRIVATE own db succeeds after the
//	    key is registered (dolt creds flow, no --user);
//	(6) WhoAmI answers with the account identity for a keypair JWT;
//	(7) an ACL RO grantee can clone but not push (private repo stays "found").
//	(7) an ACL RO grantee can clone but not push (private repo stays "found");
//	(8) push-to-create provisions a repo + empty store on the first push;
//	(9) push-to-create is refused in another user's namespace;
//	(10) a database provisioned empty ahead of time (the web form, or git.sr.ht's
//	     hook via /internal/repos) accepts a first push from an unrelated local
//	     history with NO --force.
//
// Run with:
//


@@ 29,6 34,7 @@ import (
	"encoding/base64"
	"encoding/hex"
	"fmt"
	"log/slog"
	"net"
	"os"
	"os/exec"


@@ 154,9 160,15 @@ func TestRemoteAPIIntegration(t *testing.T) {
	if err != nil {
		t.Fatalf("New: %v", err)
	}
	// The serve loops outlive the test body (GracefulStop is deferred), so their
	// errors go to slog rather than to t: a t.Errorf after the test returns
	// panics the run. This used to be a package-level logrus `logger` that the
	// slog migration removed, which left this file failing to COMPILE — and
	// nothing noticed, because the integration tag keeps it out of every default
	// build and vet run.
	go func() {
		if err := srv.Serve(); err != nil {
			logger.Errorf("remotesapi serve: %v", err)
			slog.Error("remotesapi serve", "err", err)
		}
	}()
	defer srv.GracefulStop()


@@ 167,7 179,7 @@ func TestRemoteAPIIntegration(t *testing.T) {
	}
	go func() {
		if err := csrv.Serve(); err != nil {
			logger.Errorf("credentials serve: %v", err)
			slog.Error("credentials serve", "err", err)
		}
	}()
	defer csrv.GracefulStop()


@@ 395,6 407,52 @@ func TestRemoteAPIIntegration(t *testing.T) {
		}
		t.Logf("foreign-namespace push-to-create correctly denied:\n%s", out)
	})

	// (10) A database provisioned ahead of the first push — the web create form,
	// or git.sr.ht's post-update hook through /internal/repos — is created with
	// an EMPTY store, and that is what lets its owner push a database they
	// already have without --force. The local side here is deliberately not a
	// fresh clone: it is an independent `dolt init` with its own root commit,
	// the exact shape of a beads tracker or any locally grown database. With an
	// "Initialize data repository" commit on the remote this push is a
	// non-fast-forward and dolt refuses it client-side, which is the regression
	// this test exists to catch.
	t.Run("provisioned_empty_accepts_unrelated_history", func(t *testing.T) {
		emptyAbs := storage.RepoDiskPath(reposRoot, "alice", "provisioned")
		if err := storage.InitEmptyStore(ctx, emptyAbs); err != nil {
			t.Fatalf("provision empty store: %v", err)
		}
		if _, err := store.CreateRepo(ctx, &core.Repo{
			Name: "provisioned", OwnerID: 1, OwnerName: "alice",
			Path: emptyAbs, Visibility: core.VisibilityPrivate,
		}); err != nil {
			t.Fatalf("create provisioned repo row: %v", err)
		}

		provURL := fmt.Sprintf("http://%s/~alice/provisioned", addr)
		env := append(append([]string{}, baseEnv...), "DOLT_REMOTE_PASSWORD="+pat)

		work := filepath.Join(t.TempDir(), "provisioned")
		if err := os.MkdirAll(work, 0o755); err != nil {
			t.Fatal(err)
		}
		runDolt(t, env, work, "init")
		runDolt(t, env, work, "sql", "-q", "create table beads(id int primary key); insert into beads values (7);")
		runDolt(t, env, work, "commit", "-Am", "local history that predates the remote")
		runDolt(t, env, work, "remote", "add", "origin", provURL)

		// No --force: runDolt fails the test on a non-zero exit, so a rejected
		// non-fast-forward is the failure.
		runDolt(t, env, work, "push", "--user", "alice", "origin", "main")

		dir := t.TempDir()
		runDolt(t, env, dir, "clone", "--user", "alice", provURL)
		out := runDolt(t, env, filepath.Join(dir, "provisioned"), "sql", "-q", "select id from beads", "-r", "csv")
		if !strings.Contains(out, "7") {
			t.Fatalf("pushed row not present after clone; got:\n%s", out)
		}
		t.Log("pre-provisioned empty database accepted an unrelated history without --force")
	})
}

// --- helpers ---