Skip to content
quarry Artifact [2a8c626577]
Login

Artifact [2a8c626577]

Artifact 2a8c62657752a0f9e5ee7af46f970db71f63f12e8931723aba6fae480aabc5cf:


package oltp

import (
	"errors"
	"testing"
)

func countRows(t *testing.T, db *DB, q string, args ...any) int {
	t.Helper()
	var n int
	if err := db.db.QueryRow(q, args...).Scan(&n); err != nil {
		t.Fatal(err)
	}
	return n
}

func TestDeleteRefusedWhileOwningRepos(t *testing.T) {
	db := testDB(t)
	if err := db.AddAccount("bob", "pw-bob-12345", false); err != nil {
		t.Fatal(err)
	}
	if err := db.AddRepo("bobrepo", t.TempDir(), "", "public"); err != nil {
		t.Fatal(err)
	}
	if err := db.SetOwner("bobrepo", "bob"); err != nil {
		t.Fatal(err)
	}
	err := db.DeleteAccount("bob")
	var ref Refusal
	if !errors.As(err, &ref) {
		t.Fatalf("delete with owned repos: want Refusal, got %v", err)
	}
	if _, ok := db.AccountByName("bob"); !ok {
		t.Fatal("refused delete still removed the account")
	}
	if db.Tombstoned("bob") {
		t.Fatal("refused delete tombstoned the name")
	}
	// Once transferred, the delete goes through.
	if err := db.SetOwner("bobrepo", ""); err != nil {
		t.Fatal(err)
	}
	if err := db.DeleteAccount("bob"); err != nil {
		t.Fatal(err)
	}
}

func TestDeletedAccountStaysDeleted(t *testing.T) {
	db := testDB(t)
	if err := db.AddAccount("admin", "pw-admin-12345", true); err != nil {
		t.Fatal(err)
	}
	if err := db.AddAccount("bob", "pw-bob-12345", false); err != nil {
		t.Fatal(err)
	}
	if err := db.AddRepo("shared", t.TempDir(), "", "public"); err != nil {
		t.Fatal(err)
	}
	if err := db.Grant("bob", "shared", RepoAdmin); err != nil {
		t.Fatal(err)
	}
	if err := db.Grant("admin", "shared", RepoAdmin); err != nil {
		t.Fatal(err)
	}
	value, tok, err := db.MintToken("bob", TokenSpec{Name: "t", Scopes: []string{ScopeSync}})
	if err != nil {
		t.Fatal(err)
	}
	// RFC 0021 leftovers: a migrated push row mirroring a sync_token row
	// kept for rollback, a spent secret, and a recorded carrier.
	for _, q := range []string{
		`CREATE TABLE sync_token (user TEXT PRIMARY KEY, sealed TEXT NOT NULL, created TEXT NOT NULL)`,
		`INSERT INTO sync_token VALUES ('bob', 'sealed', 'x')`,
		`UPDATE token SET migrated = 1, verifier = 'sealed', ended_secret = 'old' WHERE account = 'bob'`,
		`INSERT INTO token (id, account, name, verifier, sealed, scope, created, expires, migrated, ended_secret) VALUES ('t2', 'bob', 'push', 'v2', 1, 'sync', 'x', 'y', 1, 'old2')`,
		`INSERT INTO artifact_carrier (repo, artifact, login, at) VALUES ('shared', 'abc', 'bob', 'x')`,
	} {
		if _, err := db.w.Exec(q); err != nil {
			t.Fatal(err)
		}
	}
	sess, _, ok := db.NewSession("bob", "pw-bob-12345")
	if !ok {
		t.Fatal("no session")
	}
	if _, err := db.SetPendingEmail("bob", "[email protected]"); err != nil {
		t.Fatal(err)
	}
	if _, err := db.ResetPassword("bob"); err != nil {
		t.Fatal(err)
	}
	if _, err := db.w.Exec(`INSERT INTO team (name, created) VALUES ('crew', 'x')`); err != nil {
		t.Fatal(err)
	}
	if _, err := db.w.Exec(`INSERT INTO team_member (team, account) VALUES ('crew', 'bob')`); err != nil {
		t.Fatal(err)
	}

	if err := db.DeleteAccount("bob"); err != nil {
		t.Fatal(err)
	}

	if _, ok := db.SessionAccount(sess); ok {
		t.Error("session survived the delete")
	}
	if _, ok := db.ResolveToken("bob", value, ScopeSync); ok {
		t.Error("token survived the delete")
	}
	for table, col := range map[string]string{
		"token": "account", "session": "account", "repo_caps": "account",
		"team_member": "account", "sync_token": "user", "artifact_carrier": "login", "email_token": "account", "invite": "account",
	} {
		if n := countRows(t, db, `SELECT count(*) FROM `+table+` WHERE `+col+` = 'bob'`); n != 0 {
			t.Errorf("%s still has %d rows for bob", table, n)
		}
	}
	_ = tok

	if !db.Tombstoned("bob") {
		t.Fatal("name not tombstoned")
	}
	var ref Refusal
	if err := db.AddAccount("bob", "pw-bob-67890", false); !errors.As(err, &ref) {
		t.Errorf("re-create: want Refusal, got %v", err)
	}
	if _, err := db.InviteAccount("bob", "[email protected]", false); !errors.As(err, &ref) {
		t.Errorf("re-invite: want Refusal, got %v", err)
	}
	if _, ok := db.AccountByName("bob"); ok {
		t.Error("bob exists again")
	}
	if _, ok := db.ResolveToken("bob", value, ScopeSync); ok {
		t.Error("old token resolves")
	}
	if n := countRows(t, db, `SELECT count(*) FROM repo_caps WHERE account = 'bob'`); n != 0 {
		t.Error("grants reattached")
	}
	// A fossil user linked to bob cannot be re-linked either.
	if _, _, err := db.ReplaceFossilUsers("shared", []FossilUser{{Login: "bob", Caps: "o"}}); err != nil {
		t.Fatal(err)
	}
	if _, err := db.LinkFossilUser("admin", "shared", "bob", "bob"); err == nil {
		t.Error("fossil link to a deleted name succeeded")
	}
}