josie / simplegit

package db

import (
	"errors"
	"testing"
)

func TestCreatePullNumbersPerRepo(t *testing.T) {
	database := openTestDB(t)
	ownerID, _ := CreateUser(database, "josie", "hash")
	repoA, _ := CreateRepo(database, ownerID, "alpha", "", "public")
	repoB, _ := CreateRepo(database, ownerID, "beta", "", "public")

	first, err := CreatePull(database, repoA.ID, "one", "body", "main", "feature", ownerID, "josie", "", false)
	if err != nil {
		t.Fatalf("CreatePull A1: %v", err)
	}
	second, err := CreatePull(database, repoA.ID, "two", "body", "main", "feature2", ownerID, "josie", "", false)
	if err != nil {
		t.Fatalf("CreatePull A2: %v", err)
	}
	other, err := CreatePull(database, repoB.ID, "other", "body", "main", "feature", 0, "guest", "[EMAIL]", true)
	if err != nil {
		t.Fatalf("CreatePull B1: %v", err)
	}

	if first.Number != 1 || second.Number != 2 || other.Number != 1 {
		t.Errorf("numbers = %d, %d, %d, want 1, 2, 1", first.Number, second.Number, other.Number)
	}
	if first.State != "open" {
		t.Errorf("default state = %q, want open", first.State)
	}
	if !other.Pending || other.AuthorID.Valid {
		t.Errorf("guest pull = %+v, want pending with NULL author_id", other)
	}
	if !first.AuthorID.Valid || first.AuthorID.Int64 != ownerID {
		t.Errorf("owner pull AuthorID = %+v, want %d", first.AuthorID, ownerID)
	}
	if _, err := GetPullByNumber(database, repoB.ID, 2); !errors.Is(err, ErrNotFound) {
		t.Errorf("cross-repo pull err = %v, want ErrNotFound", err)
	}
}

func TestListPullsFilters(t *testing.T) {
	database := openTestDB(t)
	ownerID, _ := CreateUser(database, "josie", "hash")
	repo, _ := CreateRepo(database, ownerID, "alpha", "", "public")

	open, _ := CreatePull(database, repo.ID, "open", "", "main", "feature", ownerID, "josie", "", false)
	closed, _ := CreatePull(database, repo.ID, "closed", "", "main", "feature2", ownerID, "josie", "", false)
	merged, _ := CreatePull(database, repo.ID, "merged", "", "main", "feature3", ownerID, "josie", "", false)
	pending, _ := CreatePull(database, repo.ID, "pending", "", "main", "feature4", 0, "guest", "", true)

	if err := SetPullState(database, repo.ID, closed.Number, "closed"); err != nil {
		t.Fatalf("SetPullState closed: %v", err)
	}
	if err := SetPullMerged(database, repo.ID, merged.Number, "deadbeef"); err != nil {
		t.Fatalf("SetPullMerged: %v", err)
	}

	public, err := ListPulls(database, repo.ID, false, "")
	if err != nil {
		t.Fatalf("ListPulls public: %v", err)
	}
	if len(public) != 3 {
		t.Errorf("public pulls = %d, want 3 (pending hidden)", len(public))
	}
	owner, _ := ListPulls(database, repo.ID, true, "")
	if len(owner) != 4 || !owner[0].Pending {
		t.Errorf("owner pulls = %+v, want 4 with pending first", owner)
	}

	openList, _ := ListPulls(database, repo.ID, true, "open")
	if len(openList) != 2 {
		t.Errorf("open pulls = %d, want 2 (open + pending)", len(openList))
	}
	closedList, _ := ListPulls(database, repo.ID, true, "closed")
	if len(closedList) != 1 || closedList[0].Number != closed.Number {
		t.Errorf("closed pulls = %+v, want the closed one", closedList)
	}
	mergedList, _ := ListPulls(database, repo.ID, true, "merged")
	if len(mergedList) != 1 || mergedList[0].MergeCommit != "deadbeef" || !mergedList[0].MergedAt.Valid {
		t.Errorf("merged pulls = %+v, want merged with commit and timestamp", mergedList)
	}
	_ = open
	_ = pending
}

func TestSetPullStateReopen(t *testing.T) {
	database := openTestDB(t)
	ownerID, _ := CreateUser(database, "josie", "hash")
	repo, _ := CreateRepo(database, ownerID, "alpha", "", "public")
	pull, _ := CreatePull(database, repo.ID, "t", "", "main", "feature", ownerID, "josie", "", false)

	if err := SetPullState(database, repo.ID, pull.Number, "closed"); err != nil {
		t.Fatalf("close: %v", err)
	}
	closed, _ := GetPullByNumber(database, repo.ID, pull.Number)
	if closed.State != "closed" || !closed.ClosedAt.Valid {
		t.Errorf("after close = %+v, want closed with closed_at", closed)
	}
	if err := SetPullState(database, repo.ID, pull.Number, "open"); err != nil {
		t.Fatalf("reopen: %v", err)
	}
	reopened, _ := GetPullByNumber(database, repo.ID, pull.Number)
	if reopened.ClosedAt.Valid {
		t.Error("closed_at set after reopen, want NULL")
	}
}

func TestApproveAndDeletePull(t *testing.T) {
	database := openTestDB(t)
	ownerID, _ := CreateUser(database, "josie", "hash")
	repo, _ := CreateRepo(database, ownerID, "alpha", "", "public")
	pull, _ := CreatePull(database, repo.ID, "guest pr", "", "main", "feature", 0, "guest", "", true)
	if _, err := CreatePullComment(database, pull.ID, "reply", ownerID, "josie", "", false); err != nil {
		t.Fatalf("CreatePullComment: %v", err)
	}

	if pending, _ := CountPending(database, repo.ID); pending != 1 {
		t.Errorf("CountPending = %d, want 1", pending)
	}
	if err := ApprovePull(database, repo.ID, pull.Number); err != nil {
		t.Fatalf("ApprovePull: %v", err)
	}
	got, _ := GetPullByNumber(database, repo.ID, pull.Number)
	if got.Pending {
		t.Error("pull still pending after approve")
	}

	if err := DeletePull(database, repo.ID, pull.Number); err != nil {
		t.Fatalf("DeletePull: %v", err)
	}
	if _, err := GetPullByNumber(database, repo.ID, pull.Number); !errors.Is(err, ErrNotFound) {
		t.Errorf("pull after delete err = %v, want ErrNotFound", err)
	}
	comments, _ := ListPullComments(database, pull.ID, true)
	if len(comments) != 0 {
		t.Errorf("comments after cascade = %d, want 0", len(comments))
	}
}

func TestPullCommentsModeration(t *testing.T) {
	database := openTestDB(t)
	ownerID, _ := CreateUser(database, "josie", "hash")
	repo, _ := CreateRepo(database, ownerID, "alpha", "", "public")
	pull, _ := CreatePull(database, repo.ID, "t", "", "main", "feature", ownerID, "josie", "", false)

	ownerComment, _ := CreatePullComment(database, pull.ID, "owner", ownerID, "josie", "", false)
	guestComment, _ := CreatePullComment(database, pull.ID, "guest", 0, "guest", "", true)

	public, _ := ListPullComments(database, pull.ID, false)
	if len(public) != 1 || public[0].ID != ownerComment.ID {
		t.Errorf("public comments = %+v, want only the owner one", public)
	}
	if err := ApprovePullComment(database, pull.ID, guestComment.ID); err != nil {
		t.Fatalf("ApprovePullComment: %v", err)
	}
	if pending, _ := CountPending(database, repo.ID); pending != 0 {
		t.Errorf("CountPending after approve = %d, want 0", pending)
	}
	if err := DeletePullComment(database, pull.ID, guestComment.ID); err != nil {
		t.Fatalf("DeletePullComment: %v", err)
	}
	if err := ApprovePullComment(database, pull.ID+999, ownerComment.ID); !errors.Is(err, ErrNotFound) {
		t.Errorf("approve wrong pull err = %v, want ErrNotFound", err)
	}
}