@@ 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 ---