Skip to content
quarry Artifact [bc963b097d]
Login

Artifact [bc963b097d]

Artifact bc963b097d66a1a46feb0b4f76a2a0942ff3706aff4a7d89ed5d33fefe7c6f30:


package oltp

import "testing"

func str(s string) *string { return &s }
func flag(b bool) *bool    { return &b }

// A patch writes the fields it names, leaves the rest alone, and
// records one row per field that actually changed -- with the before
// value read inside the same transaction as the write.
func TestPatchRepoWritesAndRecordsOnlyWhatChanged(t *testing.T) {
	db := testDB(t)
	if err := db.AddRepo("proj", t.TempDir(), "the original", Public); err != nil {
		t.Fatal(err)
	}
	if err := db.SetRepoTopics("proj", "forge zit"); err != nil {
		t.Fatal(err)
	}

	changes, err := db.PatchRepo("proj", RepoPatch{Description: str("rewritten")}, "dmestas")
	if err != nil {
		t.Fatal(err)
	}
	if len(changes) != 1 {
		t.Fatalf("want one recorded change, got %d: %+v", len(changes), changes)
	}
	c := changes[0]
	if c.Field != "description" || c.Was != "the original" || c.Now != "rewritten" {
		t.Errorf("change = %+v, want description the original -> rewritten", c)
	}
	if c.Actor != "dmestas" {
		t.Errorf("actor = %q, want dmestas: the log cannot answer WHO", c.Actor)
	}

	r, ok, err := db.RepoByName("proj")
	if err != nil || !ok {
		t.Fatalf("RepoByName: ok=%v err=%v", ok, err)
	}
	if r.Description != "rewritten" {
		t.Errorf("description = %q: the named field was not written", r.Description)
	}
	if r.Topics != "forge zit" {
		t.Errorf("topics = %q: a description-only patch rewrote another column", r.Topics)
	}

	logged, err := db.SettingsLog("proj", 10)
	if err != nil {
		t.Fatal(err)
	}
	if len(logged) != 1 || logged[0].Field != "description" || logged[0].Was != "the original" {
		t.Errorf("SettingsLog = %+v, want the one description change", logged)
	}
	if logged[0].At == "" {
		t.Error("the log row carries no timestamp, so it cannot be lined up against anything")
	}
}

// Setting a field to the value it already holds is not a change: no
// write, no log row. Only the transaction can know that.
func TestPatchRepoIgnoresANoOp(t *testing.T) {
	db := testDB(t)
	if err := db.AddRepo("proj", t.TempDir(), "same", Public); err != nil {
		t.Fatal(err)
	}
	changes, err := db.PatchRepo("proj", RepoPatch{Description: str("same")}, "dmestas")
	if err != nil {
		t.Fatalf("a no-op patch must not be an error -- it arrived and changed nothing: %v", err)
	}
	if len(changes) != 0 {
		t.Errorf("a no-op patch recorded %+v", changes)
	}
	logged, _ := db.SettingsLog("proj", 10)
	if len(logged) != 0 {
		t.Errorf("a no-op patch wrote an audit row: %+v", logged)
	}
}

// Mirrorable is skipped for the WRITE while private and never for the
// read. The comparison is against the STORED flag: comparing against
// the effective answer is what laundered "false because private" into
// "false because asked" and left a public repository unclonable.
func TestPatchRepoSkipsMirrorableWhilePrivate(t *testing.T) {
	db := testDB(t)
	if err := db.AddRepo("vault", t.TempDir(), "", Private); err != nil {
		t.Fatal(err)
	}
	changes, err := db.PatchRepo("vault",
		RepoPatch{Mirrorable: flag(false), Description: str("still writable")}, "dmestas")
	if err != nil {
		t.Fatal(err)
	}
	for _, c := range changes {
		if c.Field == "mirrorable" {
			t.Error("mirrorable was written while the repository is private: a disabled " +
				"checkbox's value is not the admin's intent")
		}
	}
	r, _, _ := db.RepoByName("vault")
	if !r.Mirrorable {
		t.Error("the stored mirrorable flag was zeroed during a private detour, so the " +
			"repository comes back public and unclonable")
	}
	if r.Description != "still writable" {
		t.Errorf("description = %q: skipping mirrorable also skipped the rest of the patch",
			r.Description)
	}
}

// A patch against a repository with no row is refused, not reported as
// a successful no-op.
func TestPatchRepoRefusesAnUnknownRepository(t *testing.T) {
	db := testDB(t)
	if _, err := db.PatchRepo("ghost", RepoPatch{Description: str("x")}, "dmestas"); err == nil {
		t.Error("a patch against an unregistered repository reported success")
	}
}

// Taking a repository private still carries the RFC 0004 category
// strings with it -- that half writes a different table and runs after
// the commit, and dropping it would leave a private repository readable.
func TestPatchRepoCarriesVisibilityCategories(t *testing.T) {
	db := testDB(t)
	if err := db.AddRepo("proj", t.TempDir(), "", Public); err != nil {
		t.Fatal(err)
	}
	if _, err := db.PatchRepo("proj", RepoPatch{Visibility: str(Private)}, "dmestas"); err != nil {
		t.Fatal(err)
	}
	if got := db.VisibilityName("proj"); got != "Private" {
		t.Errorf("visibility name = %q, want Private: the category half of the change was "+
			"dropped, so the repository reads private but is not", got)
	}
}